<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 6.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha256-Z1K5uhUaJXA7Ll0XrZ/0JhX4lAtZFpT6jkKrEDT0drU=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"example.com","root":"/","images":"/images","scheme":"Muse","darkmode":false,"version":"8.14.1","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":-1,"unescape":false,"preload":false}}</script><script src="/js/config.js"></script>

    <meta property="og:type" content="website">
<meta property="og:title" content="JsyBlog">
<meta property="og:url" content="http://example.com/page/24/index.html">
<meta property="og:site_name" content="JsyBlog">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="SongyangJi">
<meta name="twitter:card" content="summary">


<link rel="canonical" href="http://example.com/page/24/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":true,"isPost":false,"lang":"zh-CN","comments":"","permalink":"","path":"page/24/index.html","title":""}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>JsyBlog</title>
  








  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">JsyBlog</h1>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close" role="button">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-overview-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">SongyangJi</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">251</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">45</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/tags/">
        <span class="site-state-item-count">109</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>

        </div>
      </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner index posts-expand">

    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/08/08/MyBatis%E6%BA%90%E7%A0%81%E5%89%96%E6%9E%90%20%E2%80%94%E2%80%94%20mapping-%E5%8C%85%E7%9B%B8%E5%85%B3/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/08/08/MyBatis%E6%BA%90%E7%A0%81%E5%89%96%E6%9E%90%20%E2%80%94%E2%80%94%20mapping-%E5%8C%85%E7%9B%B8%E5%85%B3/" class="post-title-link" itemprop="url">MyBatis源码剖析 —— mapping 包相关</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-08-08 00:00:00" itemprop="dateCreated datePublished" datetime="2021-08-08T00:00:00+08:00">2021-08-08</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-08-13 19:28:00" itemprop="dateModified" datetime="2021-08-13T19:28:00+08:00">2021-08-13</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/MyBatis/" itemprop="url" rel="index"><span itemprop="name">MyBatis</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.apache.ibatis.mapping;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">MappedStatement</span> &#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> String resource;</span><br><span class="line">  <span class="keyword">private</span> Configuration configuration;</span><br><span class="line">  <span class="keyword">private</span> String id;</span><br><span class="line">  <span class="keyword">private</span> Integer fetchSize;</span><br><span class="line">  <span class="keyword">private</span> Integer timeout;</span><br><span class="line">  <span class="keyword">private</span> StatementType statementType;</span><br><span class="line">  <span class="keyword">private</span> ResultSetType resultSetType;</span><br><span class="line">  <span class="keyword">private</span> SqlSource sqlSource;</span><br><span class="line">  <span class="keyword">private</span> Cache cache;</span><br><span class="line">  <span class="keyword">private</span> ParameterMap parameterMap;</span><br><span class="line">  <span class="keyword">private</span> List&lt;ResultMap&gt; resultMaps;</span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span> flushCacheRequired;</span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span> useCache;</span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span> resultOrdered;</span><br><span class="line">  <span class="keyword">private</span> SqlCommandType sqlCommandType;</span><br><span class="line">  <span class="keyword">private</span> KeyGenerator keyGenerator;</span><br><span class="line">  <span class="keyword">private</span> String[] keyProperties;</span><br><span class="line">  <span class="keyword">private</span> String[] keyColumns;</span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span> hasNestedResultMaps;</span><br><span class="line">  <span class="keyword">private</span> String databaseId;</span><br><span class="line">  <span class="keyword">private</span> Log statementLog;</span><br><span class="line">  <span class="keyword">private</span> LanguageDriver lang;</span><br><span class="line">  <span class="keyword">private</span> String[] resultSets;</span><br><span class="line"></span><br><span class="line">  MappedStatement() &#123;</span><br><span class="line">    <span class="comment">// constructor disabled</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Builder</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">MappedStatement</span> <span class="variable">mappedStatement</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MappedStatement</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Builder</span><span class="params">(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType)</span> &#123;</span><br><span class="line">      mappedStatement.configuration = configuration;</span><br><span class="line">      mappedStatement.id = id;</span><br><span class="line">      mappedStatement.sqlSource = sqlSource;</span><br><span class="line">      mappedStatement.statementType = StatementType.PREPARED;</span><br><span class="line">      mappedStatement.resultSetType = ResultSetType.DEFAULT;</span><br><span class="line">      mappedStatement.parameterMap = <span class="keyword">new</span> <span class="title class_">ParameterMap</span>.Builder(configuration, <span class="string">&quot;defaultParameterMap&quot;</span>, <span class="literal">null</span>, <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;()).build();</span><br><span class="line">      mappedStatement.resultMaps = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">      mappedStatement.sqlCommandType = sqlCommandType;</span><br><span class="line">      mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() &amp;&amp; SqlCommandType.INSERT.equals(sqlCommandType) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;</span><br><span class="line">      <span class="type">String</span> <span class="variable">logId</span> <span class="operator">=</span> id;</span><br><span class="line">      <span class="keyword">if</span> (configuration.getLogPrefix() != <span class="literal">null</span>) &#123;</span><br><span class="line">        logId = configuration.getLogPrefix() + id;</span><br><span class="line">      &#125;</span><br><span class="line">      mappedStatement.statementLog = LogFactory.getLog(logId);</span><br><span class="line">      mappedStatement.lang = configuration.getDefaultScriptingLanguageInstance();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Builder <span class="title function_">resource</span><span class="params">(String resource)</span> &#123;</span><br><span class="line">      mappedStatement.resource = resource;</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">id</span><span class="params">()</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> mappedStatement.id;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Builder <span class="title function_">parameterMap</span><span class="params">(ParameterMap parameterMap)</span> &#123;</span><br><span class="line">      mappedStatement.parameterMap = parameterMap;</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Builder <span class="title function_">resultMaps</span><span class="params">(List&lt;ResultMap&gt; resultMaps)</span> &#123;</span><br><span class="line">      mappedStatement.resultMaps = resultMaps;</span><br><span class="line">      <span class="keyword">for</span> (ResultMap resultMap : resultMaps) &#123;</span><br><span class="line">        mappedStatement.hasNestedResultMaps = mappedStatement.hasNestedResultMaps || resultMap.hasNestedResultMaps();</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Builder <span class="title function_">fetchSize</span><span class="params">(Integer fetchSize)</span> &#123;</span><br><span class="line">      mappedStatement.fetchSize = fetchSize;</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Builder <span class="title function_">timeout</span><span class="params">(Integer timeout)</span> &#123;</span><br><span class="line">      mappedStatement.timeout = timeout;</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Builder <span class="title function_">statementType</span><span class="params">(StatementType statementType)</span> &#123;</span><br><span class="line">      mappedStatement.statementType = statementType;</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Builder <span class="title function_">resultSetType</span><span class="params">(ResultSetType resultSetType)</span> &#123;</span><br><span class="line">      mappedStatement.resultSetType = resultSetType == <span class="literal">null</span> ? ResultSetType.DEFAULT : resultSetType;</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Builder <span class="title function_">cache</span><span class="params">(Cache cache)</span> &#123;</span><br><span class="line">      mappedStatement.cache = cache;</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Builder <span class="title function_">flushCacheRequired</span><span class="params">(<span class="type">boolean</span> flushCacheRequired)</span> &#123;</span><br><span class="line">      mappedStatement.flushCacheRequired = flushCacheRequired;</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Builder <span class="title function_">useCache</span><span class="params">(<span class="type">boolean</span> useCache)</span> &#123;</span><br><span class="line">      mappedStatement.useCache = useCache;</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Builder <span class="title function_">resultOrdered</span><span class="params">(<span class="type">boolean</span> resultOrdered)</span> &#123;</span><br><span class="line">      mappedStatement.resultOrdered = resultOrdered;</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Builder <span class="title function_">keyGenerator</span><span class="params">(KeyGenerator keyGenerator)</span> &#123;</span><br><span class="line">      mappedStatement.keyGenerator = keyGenerator;</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Builder <span class="title function_">keyProperty</span><span class="params">(String keyProperty)</span> &#123;</span><br><span class="line">      mappedStatement.keyProperties = delimitedStringToArray(keyProperty);</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Builder <span class="title function_">keyColumn</span><span class="params">(String keyColumn)</span> &#123;</span><br><span class="line">      mappedStatement.keyColumns = delimitedStringToArray(keyColumn);</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Builder <span class="title function_">databaseId</span><span class="params">(String databaseId)</span> &#123;</span><br><span class="line">      mappedStatement.databaseId = databaseId;</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Builder <span class="title function_">lang</span><span class="params">(LanguageDriver driver)</span> &#123;</span><br><span class="line">      mappedStatement.lang = driver;</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Builder <span class="title function_">resultSets</span><span class="params">(String resultSet)</span> &#123;</span><br><span class="line">      mappedStatement.resultSets = delimitedStringToArray(resultSet);</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Resul sets.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> resultSet</span></span><br><span class="line"><span class="comment">     *          the result set</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> the builder</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@deprecated</span> Use &#123;<span class="doctag">@link</span> #resultSets&#125;</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Deprecated</span></span><br><span class="line">    <span class="keyword">public</span> Builder <span class="title function_">resulSets</span><span class="params">(String resultSet)</span> &#123;</span><br><span class="line">      mappedStatement.resultSets = delimitedStringToArray(resultSet);</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> MappedStatement <span class="title function_">build</span><span class="params">()</span> &#123;</span><br><span class="line">      <span class="keyword">assert</span> mappedStatement.configuration != <span class="literal">null</span>;</span><br><span class="line">      <span class="keyword">assert</span> mappedStatement.id != <span class="literal">null</span>;</span><br><span class="line">      <span class="keyword">assert</span> mappedStatement.sqlSource != <span class="literal">null</span>;</span><br><span class="line">      <span class="keyword">assert</span> mappedStatement.lang != <span class="literal">null</span>;</span><br><span class="line">      mappedStatement.resultMaps = Collections.unmodifiableList(mappedStatement.resultMaps);</span><br><span class="line">      <span class="keyword">return</span> mappedStatement;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> KeyGenerator <span class="title function_">getKeyGenerator</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> keyGenerator;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> SqlCommandType <span class="title function_">getSqlCommandType</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> sqlCommandType;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">getResource</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> resource;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> Configuration <span class="title function_">getConfiguration</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> configuration;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">getId</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> id;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">hasNestedResultMaps</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> hasNestedResultMaps;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> Integer <span class="title function_">getFetchSize</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> fetchSize;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> Integer <span class="title function_">getTimeout</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> timeout;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> StatementType <span class="title function_">getStatementType</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> statementType;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> ResultSetType <span class="title function_">getResultSetType</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> resultSetType;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> SqlSource <span class="title function_">getSqlSource</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> sqlSource;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> ParameterMap <span class="title function_">getParameterMap</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> parameterMap;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> List&lt;ResultMap&gt; <span class="title function_">getResultMaps</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> resultMaps;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> Cache <span class="title function_">getCache</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> cache;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFlushCacheRequired</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> flushCacheRequired;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isUseCache</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> useCache;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isResultOrdered</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> resultOrdered;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">getDatabaseId</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> databaseId;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> String[] getKeyProperties() &#123;</span><br><span class="line">    <span class="keyword">return</span> keyProperties;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> String[] getKeyColumns() &#123;</span><br><span class="line">    <span class="keyword">return</span> keyColumns;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> Log <span class="title function_">getStatementLog</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> statementLog;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> LanguageDriver <span class="title function_">getLang</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> lang;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> String[] getResultSets() &#123;</span><br><span class="line">    <span class="keyword">return</span> resultSets;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * Gets the resul sets.</span></span><br><span class="line"><span class="comment">   *</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@return</span> the resul sets</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@deprecated</span> Use &#123;<span class="doctag">@link</span> #getResultSets()&#125;</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="meta">@Deprecated</span></span><br><span class="line">  <span class="keyword">public</span> String[] getResulSets() &#123;</span><br><span class="line">    <span class="keyword">return</span> resultSets;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> BoundSql <span class="title function_">getBoundSql</span><span class="params">(Object parameterObject)</span> &#123;</span><br><span class="line">    <span class="type">BoundSql</span> <span class="variable">boundSql</span> <span class="operator">=</span> sqlSource.getBoundSql(parameterObject);</span><br><span class="line">    List&lt;ParameterMapping&gt; parameterMappings = boundSql.getParameterMappings();</span><br><span class="line">    <span class="keyword">if</span> (parameterMappings == <span class="literal">null</span> || parameterMappings.isEmpty()) &#123;</span><br><span class="line">      boundSql = <span class="keyword">new</span> <span class="title class_">BoundSql</span>(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// check for nested result maps in parameter mappings (issue #30)</span></span><br><span class="line">    <span class="keyword">for</span> (ParameterMapping pm : boundSql.getParameterMappings()) &#123;</span><br><span class="line">      <span class="type">String</span> <span class="variable">rmId</span> <span class="operator">=</span> pm.getResultMapId();</span><br><span class="line">      <span class="keyword">if</span> (rmId != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="type">ResultMap</span> <span class="variable">rm</span> <span class="operator">=</span> configuration.getResultMap(rmId);</span><br><span class="line">        <span class="keyword">if</span> (rm != <span class="literal">null</span>) &#123;</span><br><span class="line">          hasNestedResultMaps |= rm.hasNestedResultMaps();</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> boundSql;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> String[] delimitedStringToArray(String in) &#123;</span><br><span class="line">    <span class="keyword">if</span> (in == <span class="literal">null</span> || in.trim().length() == <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> in.split(<span class="string">&quot;,&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/08/08/MyBatis%E6%BA%90%E7%A0%81%E5%89%96%E6%9E%90%20%E2%80%94%E2%80%94%20executor%20%E5%8C%85%E7%9B%B8%E5%85%B3%20%E2%80%94%E2%80%94%20Executor%E3%80%81Statement/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/08/08/MyBatis%E6%BA%90%E7%A0%81%E5%89%96%E6%9E%90%20%E2%80%94%E2%80%94%20executor%20%E5%8C%85%E7%9B%B8%E5%85%B3%20%E2%80%94%E2%80%94%20Executor%E3%80%81Statement/" class="post-title-link" itemprop="url">MyBatis源码剖析 —— executor 包相关之Executor、Statement</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-08-08 00:00:00" itemprop="dateCreated datePublished" datetime="2021-08-08T00:00:00+08:00">2021-08-08</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-08-13 20:12:37" itemprop="dateModified" datetime="2021-08-13T20:12:37+08:00">2021-08-13</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/MyBatis/" itemprop="url" rel="index"><span itemprop="name">MyBatis</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="Executor相关"><a href="#Executor相关" class="headerlink" title="Executor相关"></a>Executor相关</h1><p>只列出了两个有代表性的方法。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.apache.ibatis.executor;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Executor</span> &#123;</span><br><span class="line"></span><br><span class="line">  <span class="type">int</span> <span class="title function_">update</span><span class="params">(MappedStatement ms, Object parameter)</span> <span class="keyword">throws</span> SQLException;</span><br><span class="line"></span><br><span class="line">  &lt;E&gt; List&lt;E&gt; <span class="title function_">query</span><span class="params">(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql)</span> <span class="keyword">throws</span> SQLException;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>方法参数里，有一个重要的 MappedStatement， 这个实际上就是在<strong>xml里写的sql语句的包装类</strong>。</p>
<p>来看一看它的实现类</p>
<h2 id="BaseExecutor"><a href="#BaseExecutor" class="headerlink" title="BaseExecutor"></a>BaseExecutor</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.apache.ibatis.executor;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">BaseExecutor</span> <span class="keyword">implements</span> <span class="title class_">Executor</span> &#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">protected</span> Transaction transaction;</span><br><span class="line">  <span class="keyword">protected</span> Executor wrapper;</span><br><span class="line">  <span class="keyword">protected</span> Configuration configuration;</span><br><span class="line"></span><br><span class="line">  </span><br><span class="line">  <span class="comment">// executor 的 commmit、rollback 都去调 transaction 的 commit()、rollback()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">commit</span><span class="params">(<span class="type">boolean</span> required)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="keyword">if</span> (required) &#123;</span><br><span class="line">      transaction.commit();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">rollback</span><span class="params">(<span class="type">boolean</span> required)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line"> 		 <span class="comment">// ...    </span></span><br><span class="line">          transaction.rollback();</span><br><span class="line"> </span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 增删改查最终都调用这两个方法，具体实现由 ExecutorType 这个枚举量决定</span></span><br><span class="line">  <span class="keyword">protected</span> <span class="keyword">abstract</span> <span class="type">int</span> <span class="title function_">doUpdate</span><span class="params">(MappedStatement ms, Object parameter)</span> <span class="keyword">throws</span> SQLException;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">protected</span> <span class="keyword">abstract</span> &lt;E&gt; List&lt;E&gt; <span class="title function_">doQuery</span><span class="params">(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)</span></span><br><span class="line">      <span class="keyword">throws</span> SQLException;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="SimpleExecutor"><a href="#SimpleExecutor" class="headerlink" title="SimpleExecutor"></a>SimpleExecutor</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.apache.ibatis.executor;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SimpleExecutor</span> <span class="keyword">extends</span> <span class="title class_">BaseExecutor</span> &#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">SimpleExecutor</span><span class="params">(Configuration configuration, Transaction transaction)</span> &#123;</span><br><span class="line">    <span class="built_in">super</span>(configuration, transaction);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">doUpdate</span><span class="params">(MappedStatement ms, Object parameter)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">    <span class="type">Statement</span> <span class="variable">stmt</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="type">Configuration</span> <span class="variable">configuration</span> <span class="operator">=</span> ms.getConfiguration();</span><br><span class="line">      <span class="comment">// 获取 Statement 处理器， 注意这里传入了参数 MappedStatement，parameter</span></span><br><span class="line">      <span class="type">StatementHandler</span> <span class="variable">handler</span> <span class="operator">=</span> configuration.newStatementHandler(<span class="built_in">this</span>, ms, parameter, RowBounds.DEFAULT, <span class="literal">null</span>, <span class="literal">null</span>);</span><br><span class="line">      stmt = prepareStatement(handler, ms.getStatementLog());</span><br><span class="line">      <span class="comment">// 最终用 Statement处理器去处理 Statement</span></span><br><span class="line">      <span class="keyword">return</span> handler.update(stmt);</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">      closeStatement(stmt);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line"></span><br><span class="line">  <span class="comment">// 与上面一样的做法</span></span><br><span class="line">  <span class="keyword">public</span> &lt;E&gt; List&lt;E&gt; <span class="title function_">doQuery</span><span class="params">(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> Statement <span class="title function_">prepareStatement</span><span class="params">(StatementHandler handler, Log statementLog)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">    Statement stmt;</span><br><span class="line">    <span class="type">Connection</span> <span class="variable">connection</span> <span class="operator">=</span> getConnection(statementLog);</span><br><span class="line">    stmt = handler.prepare(connection, transaction.getTimeout());</span><br><span class="line">    <span class="comment">// 填充参数（如果是 Prepared型的Statement的话）</span></span><br><span class="line">    handler.parameterize(stmt);</span><br><span class="line">    <span class="keyword">return</span> stmt;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>具体的实现类其实由 ExecutorType 的三种类型确定，可以通过配置文件里的 settings 的 defaultExecutorType 来配置：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">enum</span> <span class="title class_">ExecutorType</span> &#123;</span><br><span class="line">  SIMPLE, REUSE, BATCH</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里不再细述其他两种。</p>
<h1 id="Statement相关"><a href="#Statement相关" class="headerlink" title="Statement相关"></a>Statement相关</h1><p>这个不是mybatis里定义的东西，而是<code>java.sql</code>里定义的接口。</p>
<p><strong>用于执行静态 SQL 语句并返回它产生的结果的对象。</strong></p>
<p>它有众多和数据源相关的实现类，比如<code>com.mysql.cj.jdbc.StatementImpl</code>就是和mysql交互的statement。</p>
<h2 id="StatementHandler"><a href="#StatementHandler" class="headerlink" title="StatementHandler"></a>StatementHandler</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.apache.ibatis.executor.statement;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">StatementHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 获得Statement，数据库连接、事务超时时间 </span></span><br><span class="line">  Statement <span class="title function_">prepare</span><span class="params">(Connection connection, Integer transactionTimeout)</span></span><br><span class="line">      <span class="keyword">throws</span> SQLException;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 参数化“语句”</span></span><br><span class="line">  <span class="keyword">void</span> <span class="title function_">parameterize</span><span class="params">(Statement statement)</span></span><br><span class="line">      <span class="keyword">throws</span> SQLException;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 执行 Statement 的 update 操作</span></span><br><span class="line">  <span class="type">int</span> <span class="title function_">update</span><span class="params">(Statement statement)</span></span><br><span class="line">      <span class="keyword">throws</span> SQLException;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 执行 查询操作</span></span><br><span class="line">  &lt;E&gt; List&lt;E&gt; <span class="title function_">query</span><span class="params">(Statement statement, ResultHandler resultHandler)</span></span><br><span class="line">      <span class="keyword">throws</span> SQLException;</span><br><span class="line"></span><br><span class="line">  </span><br><span class="line">  BoundSql <span class="title function_">getBoundSql</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">  ParameterHandler <span class="title function_">getParameterHandler</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="BaseStatementHandler"><a href="#BaseStatementHandler" class="headerlink" title="BaseStatementHandler"></a>BaseStatementHandler</h2><p>抽象类，实现了 StatementHandler 部分方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.apache.ibatis.executor.statement;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">BaseStatementHandler</span> <span class="keyword">implements</span> <span class="title class_">StatementHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">protected</span> <span class="keyword">final</span> Configuration configuration;</span><br><span class="line">  <span class="keyword">protected</span> <span class="keyword">final</span> ObjectFactory objectFactory;             <span class="comment">// 对象工厂，负责通过反射的方式生成对象</span></span><br><span class="line">  <span class="keyword">protected</span> <span class="keyword">final</span> TypeHandlerRegistry typeHandlerRegistry; <span class="comment">// 类型处理器工厂</span></span><br><span class="line">  <span class="keyword">protected</span> <span class="keyword">final</span> ResultSetHandler resultSetHandler;       <span class="comment">// 结果集处理器</span></span><br><span class="line">  <span class="keyword">protected</span> <span class="keyword">final</span> ParameterHandler parameterHandler;       <span class="comment">// 参数处理器</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">protected</span> <span class="keyword">final</span> Executor executor;</span><br><span class="line">  <span class="keyword">protected</span> <span class="keyword">final</span> MappedStatement mappedStatement;         <span class="comment">// 从 xml 读取来的 sql语句的包装类</span></span><br><span class="line">  <span class="keyword">protected</span> <span class="keyword">final</span> RowBounds rowBounds;        </span><br><span class="line"></span><br><span class="line">  <span class="keyword">protected</span> BoundSql boundSql;                             <span class="comment">// 处理任何Mybatis的动态sql标签后，从SqlSource获得的实际 SQL 字符串</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">protected</span> <span class="title function_">BaseStatementHandler</span><span class="params">(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)</span> &#123;</span><br><span class="line">    <span class="comment">/// ...</span></span><br><span class="line">    <span class="comment">// 从配置类获取</span></span><br><span class="line">    <span class="built_in">this</span>.typeHandlerRegistry = configuration.getTypeHandlerRegistry();</span><br><span class="line">    <span class="built_in">this</span>.objectFactory = configuration.getObjectFactory();</span><br><span class="line">    <span class="keyword">if</span> (boundSql == <span class="literal">null</span>) &#123; <span class="comment">// issue #435, get the key before calculating the statement</span></span><br><span class="line">      generateKeys(parameterObject);</span><br><span class="line">      boundSql = mappedStatement.getBoundSql(parameterObject);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">this</span>.boundSql = boundSql;</span><br><span class="line">    <span class="comment">// 从配置类获取</span></span><br><span class="line">    <span class="built_in">this</span>.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);</span><br><span class="line">    <span class="built_in">this</span>.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="keyword">public</span> Statement <span class="title function_">prepare</span><span class="params">(Connection connection, Integer transactionTimeout)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">      <span class="type">Statement</span> <span class="variable">statement</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">      <span class="comment">// 调用初始化 Statement 参数，传入参数 connection</span></span><br><span class="line">      statement = instantiateStatement(connection);</span><br><span class="line">      </span><br><span class="line">      <span class="comment">// 设置两个配置参数</span></span><br><span class="line">      setStatementTimeout(statement, transactionTimeout);</span><br><span class="line">      setFetchSize(statement);</span><br><span class="line">      <span class="keyword">return</span> statement;</span><br><span class="line">    </span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">protected</span> <span class="keyword">abstract</span> Statement <span class="title function_">instantiateStatement</span><span class="params">(Connection connection)</span> <span class="keyword">throws</span> SQLException;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">setStatementTimeout</span><span class="params">(Statement stmt, Integer transactionTimeout)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">    <span class="comment">// ... </span></span><br><span class="line">    <span class="comment">// 设置 查询时间、事务时间</span></span><br><span class="line">    StatementUtil.applyTransactionTimeout(stmt, queryTimeout, transactionTimeout);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">setFetchSize</span><span class="params">(Statement stmt)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">			<span class="comment">// 调用 stmt.setFetchSize(fetchSize);</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">closeStatement</span><span class="params">(Statement statement)</span> &#123;</span><br><span class="line">       statement.close();</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 生成主键相关</span></span><br><span class="line">  <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">generateKeys</span><span class="params">(Object parameter)</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.apache.ibatis.executor.statement;</span><br><span class="line"></span><br><span class="line"><span class="comment">// routing 路由</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RoutingStatementHandler</span> <span class="keyword">implements</span> <span class="title class_">StatementHandler</span> &#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">final</span> StatementHandler delegate;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">RoutingStatementHandler</span><span class="params">(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)</span> &#123;</span><br><span class="line">    <span class="comment">// 根据 不同的 StatementType 选择相应的StatementHandler接口的实现类 </span></span><br><span class="line">    <span class="keyword">switch</span> (ms.getStatementType()) &#123;</span><br><span class="line">      <span class="keyword">case</span> STATEMENT:</span><br><span class="line">        delegate = <span class="keyword">new</span> <span class="title class_">SimpleStatementHandler</span>(executor, ms, parameter, rowBounds, resultHandler, boundSql);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">      <span class="keyword">case</span> PREPARED:</span><br><span class="line">        delegate = <span class="keyword">new</span> <span class="title class_">PreparedStatementHandler</span>(executor, ms, parameter, rowBounds, resultHandler, boundSql);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">      <span class="keyword">case</span> CALLABLE:</span><br><span class="line">        delegate = <span class="keyword">new</span> <span class="title class_">CallableStatementHandler</span>(executor, ms, parameter, rowBounds, resultHandler, boundSql);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">      <span class="keyword">default</span>:</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ExecutorException</span>(<span class="string">&quot;Unknown statement type: &quot;</span> + ms.getStatementType());</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.apache.ibatis.executor.statement;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SimpleStatementHandler</span> <span class="keyword">extends</span> <span class="title class_">BaseStatementHandler</span> &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">update</span><span class="params">(Statement statement)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> boundSql.getSql();</span><br><span class="line">    <span class="type">Object</span> <span class="variable">parameterObject</span> <span class="operator">=</span> boundSql.getParameterObject();</span><br><span class="line">    <span class="type">KeyGenerator</span> <span class="variable">keyGenerator</span> <span class="operator">=</span> mappedStatement.getKeyGenerator();</span><br><span class="line">    <span class="type">int</span> rows;</span><br><span class="line">    <span class="comment">// 调用 Statement的 execute 方法完成任务</span></span><br><span class="line">    <span class="keyword">if</span> (keyGenerator <span class="keyword">instanceof</span> Jdbc3KeyGenerator) &#123;</span><br><span class="line">      statement.execute(sql, Statement.RETURN_GENERATED_KEYS);</span><br><span class="line">      rows = statement.getUpdateCount();</span><br><span class="line">      keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (keyGenerator <span class="keyword">instanceof</span> SelectKeyGenerator) &#123;</span><br><span class="line">      statement.execute(sql);</span><br><span class="line">      rows = statement.getUpdateCount();</span><br><span class="line">      keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      statement.execute(sql);</span><br><span class="line">      rows = statement.getUpdateCount();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> rows;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="keyword">public</span> &lt;E&gt; List&lt;E&gt; <span class="title function_">query</span><span class="params">(Statement statement, ResultHandler resultHandler)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> boundSql.getSql();</span><br><span class="line">    <span class="comment">// 这里的sql已经完全是可执行的sql了，无需参数填充</span></span><br><span class="line">    statement.execute(sql);</span><br><span class="line">    <span class="comment">// 用结果集处理器处理 statement.getResultSet()</span></span><br><span class="line">    <span class="keyword">return</span> resultSetHandler.handleResultSets(statement);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 到最后还是要 调用 JDBC 的 Connection 的 createStatement 的方法</span></span><br><span class="line">  <span class="meta">@Override</span> </span><br><span class="line">  <span class="keyword">protected</span> Statement <span class="title function_">instantiateStatement</span><span class="params">(Connection connection)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">    <span class="keyword">if</span> (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) &#123;</span><br><span class="line">      <span class="keyword">return</span> connection.createStatement();</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="comment">// JDBC 允许返回 ResultSet 的时候指定 ”结果集类型“—— ResultSetType， 这是一个枚举量</span></span><br><span class="line">      <span class="keyword">return</span> connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">parameterize</span><span class="params">(Statement statement)</span> &#123;</span><br><span class="line">    <span class="comment">// do none thing (因为这是 ”简单型“的Statement处理器, 没有对占位符#&#123;&#125;进行处理)</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.apache.ibatis.executor.statement;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PreparedStatementHandler</span> <span class="keyword">extends</span> <span class="title class_">BaseStatementHandler</span> &#123;</span><br><span class="line">  <span class="comment">// 和上面的一样，只不过把关于 keyGenerator的部分放在了 instantiateStatement 罢了</span></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">update</span><span class="params">(Statement statement)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 和上面的一样</span></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="keyword">public</span> &lt;E&gt; List&lt;E&gt; <span class="title function_">query</span><span class="params">(Statement statement, ResultHandler resultHandler)</span> <span class="keyword">throws</span> SQLException &#123; </span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="keyword">protected</span> Statement <span class="title function_">instantiateStatement</span><span class="params">(Connection connection)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> boundSql.getSql();</span><br><span class="line">    <span class="keyword">if</span> (mappedStatement.getKeyGenerator() <span class="keyword">instanceof</span> Jdbc3KeyGenerator) &#123;</span><br><span class="line">      String[] keyColumnNames = mappedStatement.getKeyColumns();</span><br><span class="line">      <span class="keyword">if</span> (keyColumnNames == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> connection.prepareStatement(sql, keyColumnNames);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) &#123;</span><br><span class="line">      <span class="comment">// 注意 这里的sql 仍然会有 占位符，所以执行流程后面还要调用 parameterize</span></span><br><span class="line">      <span class="keyword">return</span> connection.prepareStatement(sql);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">parameterize</span><span class="params">(Statement statement)</span> <span class="keyword">throws</span> SQLException &#123;</span><br><span class="line">    <span class="comment">// 相比于 SimpleStatementHandler 多了一个占位符的填充参数</span></span><br><span class="line">    parameterHandler.setParameters((PreparedStatement) statement);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/08/06/MyBatis%E6%96%87%E6%A1%A3%E6%91%98%E8%A6%81/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/08/06/MyBatis%E6%96%87%E6%A1%A3%E6%91%98%E8%A6%81/" class="post-title-link" itemprop="url">MyBatis文档摘要</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-08-06 00:00:00" itemprop="dateCreated datePublished" datetime="2021-08-06T00:00:00+08:00">2021-08-06</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-08-08 18:54:16" itemprop="dateModified" datetime="2021-08-08T18:54:16+08:00">2021-08-08</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/MyBatis/" itemprop="url" rel="index"><span itemprop="name">MyBatis</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="配置文件"><a href="#配置文件" class="headerlink" title="配置文件"></a>配置文件</h1><p>MyBatis 的配置文件的层次结构：</p>
<ul>
<li>configuration（配置）<ul>
<li>properties（属性）</li>
<li>settings（设置）</li>
<li>typeAliases（类型别名）</li>
<li>typeHandlers（类型处理器）</li>
<li>objectFactory（对象工厂）</li>
<li>plugins（插件）</li>
<li>environments（环境配置）<ul>
<li>environment（环境变量）<ul>
<li>transactionManager（事务管理器）</li>
<li>dataSource（数据源）</li>
</ul>
</li>
</ul>
</li>
<li>databaseIdProvider（数据库厂商标识）</li>
<li>mappers（映射器）</li>
</ul>
</li>
</ul>
<h2 id="属性（properties）"><a href="#属性（properties）" class="headerlink" title="属性（properties）"></a>属性（properties）</h2><h2 id="设置（settings）"><a href="#设置（settings）" class="headerlink" title="设置（settings）"></a>设置（settings）</h2><h3 id="对象关系映射相关"><a href="#对象关系映射相关" class="headerlink" title="对象关系映射相关"></a>对象关系映射相关</h3><table>
<thead>
<tr>
<th align="left">设置名</th>
<th align="left">描述</th>
<th align="left">有效值</th>
<th align="left">默认值</th>
</tr>
</thead>
<tbody><tr>
<td align="left">autoMappingBehavior</td>
<td align="left">指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射；PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集（无论是否嵌套）。</td>
<td align="left">NONE, PARTIAL, FULL</td>
<td align="left">PARTIAL</td>
</tr>
<tr>
<td align="left">mapUnderscoreToCamelCase</td>
<td align="left">是否开启驼峰命名自动映射，即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。</td>
<td align="left">true | false</td>
<td align="left">False</td>
</tr>
<tr>
<td align="left">autoMappingUnknownColumnBehavior</td>
<td align="left">指定发现自动映射目标未知列（或未知属性类型）的行为。<code>NONE</code>: 不做任何反应<code>WARNING</code>: 输出警告日志（<code>&#39;org.apache.ibatis.session.AutoMappingUnknownColumnBehavior&#39;</code> 的日志等级必须设置为 <code>WARN</code>）<code>FAILING</code>: 映射失败 (抛出 <code>SqlSessionException</code>)</td>
<td align="left">NONE, WARNING, FAILING</td>
<td align="left"></td>
</tr>
</tbody></table>
<h3 id="缓存相关"><a href="#缓存相关" class="headerlink" title="缓存相关"></a>缓存相关</h3><table>
<thead>
<tr>
<th align="left">设置名</th>
<th align="left">描述</th>
<th align="left">有效值</th>
<th>默认值</th>
</tr>
</thead>
<tbody><tr>
<td align="left">cacheEnabled</td>
<td align="left">全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。</td>
<td align="left">true | false</td>
<td>true</td>
</tr>
<tr>
<td align="left">localCacheScope</td>
<td align="left">MyBatis 利用本地缓存机制（Local Cache）防止循环引用和加速重复的嵌套查询。 默认值为 SESSION，会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT，本地缓存将仅用于执行语句，对相同 SqlSession 的不同查询将不会进行缓存。</td>
<td align="left">SESSION | STATEMENT</td>
<td>SESSION</td>
</tr>
<tr>
<td align="left"></td>
<td align="left"></td>
<td align="left"></td>
<td></td>
</tr>
</tbody></table>
<h3 id="数据库连接与执行器相关"><a href="#数据库连接与执行器相关" class="headerlink" title="数据库连接与执行器相关"></a>数据库连接与执行器相关</h3><table>
<thead>
<tr>
<th align="left">设置名</th>
<th align="left">描述</th>
<th align="left">有效值</th>
<th>默认值</th>
</tr>
</thead>
<tbody><tr>
<td align="left">defaultStatementTimeout</td>
<td align="left">设置超时时间，它决定数据库驱动等待数据库响应的秒数。</td>
<td align="left">任意正整数</td>
<td>未设置 (null)</td>
</tr>
<tr>
<td align="left">defaultExecutorType</td>
<td align="left">配置默认的执行器。SIMPLE 就是普通的执行器；REUSE 执行器会重用预处理语句（PreparedStatement）； BATCH 执行器不仅重用语句还会执行批量更新。</td>
<td align="left">SIMPLE REUSE BATCH</td>
<td>SIMPLE</td>
</tr>
<tr>
<td align="left"></td>
<td align="left"></td>
<td align="left"></td>
<td></td>
</tr>
</tbody></table>
<h3 id="日志相关"><a href="#日志相关" class="headerlink" title="日志相关"></a>日志相关</h3><table>
<thead>
<tr>
<th align="left">设置名</th>
<th align="left">描述</th>
<th align="left">有效值</th>
<th>默认值</th>
</tr>
</thead>
<tbody><tr>
<td align="left">logPrefix</td>
<td align="left">指定 MyBatis 增加到日志名称的前缀。</td>
<td align="left">任何字符串</td>
<td>未设置</td>
</tr>
<tr>
<td align="left">logImpl</td>
<td align="left">指定 MyBatis 所用日志的具体实现，未指定时将自动查找。</td>
<td align="left">SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING</td>
<td>未设置</td>
</tr>
</tbody></table>
<h2 id="类型别名（typeAliases）"><a href="#类型别名（typeAliases）" class="headerlink" title="类型别名（typeAliases）"></a>类型别名（typeAliases）</h2><h2 id="类型处理器（typeHandlers）"><a href="#类型处理器（typeHandlers）" class="headerlink" title="类型处理器（typeHandlers）"></a>类型处理器（typeHandlers）</h2>
      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/08/06/MyBatis%E6%BA%90%E7%A0%81%E5%89%96%E6%9E%90%20%E2%80%94%E2%80%94%20session%20%E5%8C%85%E7%9B%B8%E5%85%B3/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/08/06/MyBatis%E6%BA%90%E7%A0%81%E5%89%96%E6%9E%90%20%E2%80%94%E2%80%94%20session%20%E5%8C%85%E7%9B%B8%E5%85%B3/" class="post-title-link" itemprop="url">MyBatis源码剖析 —— session 包相关</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-08-06 00:00:00" itemprop="dateCreated datePublished" datetime="2021-08-06T00:00:00+08:00">2021-08-06</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-08-08 19:33:13" itemprop="dateModified" datetime="2021-08-08T19:33:13+08:00">2021-08-08</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/MyBatis/" itemprop="url" rel="index"><span itemprop="name">MyBatis</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="构建流程"><a href="#构建流程" class="headerlink" title="构建流程"></a>构建流程</h1><p>从 SqlSessionFactoryBuilder 构建 SqlSessionFactory。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> SqlSessionFactory <span class="title function_">build</span><span class="params">(InputStream inputStream, String environment, Properties properties)</span> &#123;</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="type">XMLConfigBuilder</span> <span class="variable">parser</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">XMLConfigBuilder</span>(inputStream, environment, properties);</span><br><span class="line">    <span class="keyword">return</span> build(parser.parse());</span><br><span class="line">  &#125;  </span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> SqlSessionFactory <span class="title function_">build</span><span class="params">(Configuration config)</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">DefaultSqlSessionFactory</span>(config);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h1 id="SqlSessionFactoryBuilder"><a href="#SqlSessionFactoryBuilder" class="headerlink" title="SqlSessionFactoryBuilder"></a>SqlSessionFactoryBuilder</h1><p><code>SqlSessionFactoryBuilder</code>仅用来构建SqlSessionFactory，</p>
<p>提供了一系列 build 方法（从各种来源，如输入流、配置类）等等构建SqlSessionFactory。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.apache.ibatis.session;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SqlSessionFactoryBuilder</span> &#123;</span><br><span class="line">  <span class="comment">// build()方法簇</span></span><br><span class="line">  <span class="keyword">public</span> SqlSessionFactory <span class="title function_">build</span><span class="params">(..)</span> &#123;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="SqlSessionFactory接口、实现类"><a href="#SqlSessionFactory接口、实现类" class="headerlink" title="SqlSessionFactory接口、实现类"></a>SqlSessionFactory接口、实现类</h1><p>SqlSession工厂</p>
<h2 id="SqlSessionFactory"><a href="#SqlSessionFactory" class="headerlink" title="SqlSessionFactory"></a>SqlSessionFactory</h2><p>提供了一系列openSession()方法的接口，并接受一些连接属性，如autoCommit（是否自动提交）、TransactionIsolationLevel（事务的隔离级别）等。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.apache.ibatis.session;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">SqlSessionFactory</span> &#123;</span><br><span class="line">  <span class="comment">// openSession()方法簇</span></span><br><span class="line">  SqlSession <span class="title function_">openSession</span><span class="params">(..)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


<h2 id="DefaultSqlSessionFactory"><a href="#DefaultSqlSessionFactory" class="headerlink" title="DefaultSqlSessionFactory"></a>DefaultSqlSessionFactory</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.apache.ibatis.session.defaults;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DefaultSqlSessionFactory</span> <span class="keyword">implements</span> <span class="title class_">SqlSessionFactory</span> &#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">final</span> Configuration configuration;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/*</span></span><br><span class="line"><span class="comment">   以上省略实现接口的 openSession(..) 方法，</span></span><br><span class="line"><span class="comment">   均调用了 openSessionFromDataSource openSessionFromConnection 方法</span></span><br><span class="line"><span class="comment">  */</span></span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> SqlSession <span class="title function_">openSessionFromDataSource</span><span class="params">(ExecutorType execType, TransactionIsolationLevel level, <span class="type">boolean</span> autoCommit)</span> &#123;</span><br><span class="line">    <span class="type">Transaction</span> <span class="variable">tx</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">final</span> <span class="type">Environment</span> <span class="variable">environment</span> <span class="operator">=</span> configuration.getEnvironment();</span><br><span class="line">      <span class="comment">// 获取 事务管理器</span></span><br><span class="line">      <span class="keyword">final</span> <span class="type">TransactionFactory</span> <span class="variable">transactionFactory</span> <span class="operator">=</span> getTransactionFactoryFromEnvironment(environment);</span><br><span class="line">      <span class="comment">// 指定参数： 数据源、事务隔离级别、是否自动提交，获取事务</span></span><br><span class="line">      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);</span><br><span class="line">      <span class="comment">// 获取 执行器</span></span><br><span class="line">      <span class="keyword">final</span> <span class="type">Executor</span> <span class="variable">executor</span> <span class="operator">=</span> configuration.newExecutor(tx, execType);</span><br><span class="line">      <span class="comment">// 构造 session 并返回</span></span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">DefaultSqlSession</span>(configuration, executor, autoCommit);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">      closeTransaction(tx); <span class="comment">// may have fetched a connection so lets call close()</span></span><br><span class="line">      <span class="keyword">throw</span> ExceptionFactory.wrapException(<span class="string">&quot;Error opening session.  Cause: &quot;</span> + e, e);</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">      ErrorContext.instance().reset();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> SqlSession <span class="title function_">openSessionFromConnection</span><span class="params">(ExecutorType execType, Connection connection)</span> &#123;</span><br><span class="line">    <span class="comment">// 与上面的逻辑几乎一致，不过从 java.sql.Connection 可以获得很多信息如隔离级别、是否自动提交等信息</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 根据配置信息返回 transactionManager，否则返回默认的 ManagedTransactionFactory </span></span><br><span class="line">  <span class="keyword">private</span> TransactionFactory <span class="title function_">getTransactionFactoryFromEnvironment</span><span class="params">(Environment environment)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (environment == <span class="literal">null</span> || environment.getTransactionFactory() == <span class="literal">null</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ManagedTransactionFactory</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> environment.getTransactionFactory();</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>




<h1 id="SqlSession接口、实现类"><a href="#SqlSession接口、实现类" class="headerlink" title="SqlSession接口、实现类"></a>SqlSession接口、实现类</h1><p>SqlSession 接口</p>
<h2 id="SqlSession"><a href="#SqlSession" class="headerlink" title="SqlSession"></a>SqlSession</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.apache.ibatis.session;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">只摘取了部分方法</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">SqlSession</span> <span class="keyword">extends</span> <span class="title class_">Closeable</span> &#123;</span><br><span class="line">  <span class="comment">// 各种select语句</span></span><br><span class="line">  &lt;T&gt; T <span class="title function_">selectOne</span><span class="params">(String statement)</span>;</span><br><span class="line">  &lt;E&gt; List&lt;E&gt; <span class="title function_">selectList</span><span class="params">(String statement)</span>;</span><br><span class="line">  &lt;K, V&gt; Map&lt;K, V&gt; <span class="title function_">selectMap</span><span class="params">(String statement, String mapKey)</span>;</span><br><span class="line">  &lt;T&gt; Cursor&lt;T&gt; <span class="title function_">selectCursor</span><span class="params">(String statement)</span>;</span><br><span class="line">  <span class="keyword">void</span> <span class="title function_">select</span><span class="params">(String statement, ResultHandler handler)</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 插入、更新、删除</span></span><br><span class="line">  <span class="type">int</span> <span class="title function_">insert</span><span class="params">(String statement)</span>;</span><br><span class="line">  <span class="type">int</span> <span class="title function_">update</span><span class="params">(String statement)</span>;</span><br><span class="line">  <span class="type">int</span> <span class="title function_">delete</span><span class="params">(String statement)</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 事务的提交、回滚</span></span><br><span class="line">  <span class="keyword">void</span> <span class="title function_">commit</span><span class="params">()</span>;</span><br><span class="line">  <span class="keyword">void</span> <span class="title function_">commit</span><span class="params">(<span class="type">boolean</span> force)</span>;</span><br><span class="line">  <span class="keyword">void</span> <span class="title function_">rollback</span><span class="params">()</span>;</span><br><span class="line">  <span class="keyword">void</span> <span class="title function_">rollback</span><span class="params">(<span class="type">boolean</span> force)</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 绑定到此 SqlSession 的映射器</span></span><br><span class="line">  &lt;T&gt; T <span class="title function_">getMapper</span><span class="params">(Class&lt;T&gt; type)</span>;</span><br><span class="line"> </span><br><span class="line">  Connection <span class="title function_">getConnection</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="DefaultSqlSession"><a href="#DefaultSqlSession" class="headerlink" title="DefaultSqlSession"></a>DefaultSqlSession</h2><p>SqlSession的默认实现类，注意此类线程不安全，所以同一个<code>DefaultSqlSession</code>实例不能作为多个线程共享的变量。</p>
<p>有两个重要的成员属性：configuration，executor。</p>
<p>configuration 获取绑定的sql语句，映射器等。</p>
<p>executor用于执行sql任务。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.apache.ibatis.session.defaults;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> 省略了部分字段、接口中的方法，以及异常处理，</span></span><br><span class="line"><span class="comment"> 只留下最关键的方法调用。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DefaultSqlSession</span> <span class="keyword">implements</span> <span class="title class_">SqlSession</span> &#123;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">final</span> Configuration configuration;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">final</span> Executor executor;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// select语句最终的实现方法</span></span><br><span class="line">  <span class="keyword">private</span> &lt;E&gt; List&lt;E&gt; <span class="title function_">selectList</span><span class="params">(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler)</span> &#123;</span><br><span class="line">      <span class="type">MappedStatement</span> <span class="variable">ms</span> <span class="operator">=</span> configuration.getMappedStatement(statement);</span><br><span class="line">      <span class="keyword">return</span> executor.query(ms, wrapCollection(parameter), rowBounds, handler);    </span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  </span><br><span class="line">  <span class="comment">// update、delete、insert在处理的时候其实不加区分，最后都是调用这个方法（因为不需要对结果集进行对象映射）</span></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">update</span><span class="params">(String statement, Object parameter)</span> &#123;</span><br><span class="line">      dirty = <span class="literal">true</span>;</span><br><span class="line">      <span class="type">MappedStatement</span> <span class="variable">ms</span> <span class="operator">=</span> configuration.getMappedStatement(statement);</span><br><span class="line">      <span class="keyword">return</span> executor.update(ms, wrapCollection(parameter));</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">commit</span><span class="params">(<span class="type">boolean</span> force)</span> &#123;</span><br><span class="line">      executor.commit(isCommitOrRollbackRequired(force));</span><br><span class="line">      dirty = <span class="literal">false</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">rollback</span><span class="params">(<span class="type">boolean</span> force)</span> &#123;</span><br><span class="line">      executor.rollback(isCommitOrRollbackRequired(force));</span><br><span class="line">      dirty = <span class="literal">false</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 根据 xxxMapper.class 获取 xxxMapper的实例（代理生成的对象）</span></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="keyword">public</span> &lt;T&gt; T <span class="title function_">getMapper</span><span class="params">(Class&lt;T&gt; type)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> configuration.getMapper(type, <span class="built_in">this</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="SqlSessionManager"><a href="#SqlSessionManager" class="headerlink" title="SqlSessionManager"></a>SqlSessionManager</h2><p><code>SqlSessionManager</code>相比<code>SqlSession</code>而言，一方面做到了线程安全，另一方面避免了每次都需要 openSession(), 实现了对昂贵对象的复用。</p>
<p>通过 <code>Proxy</code> + <code>ThreadLocal</code> 的方式 代理对 <code>SqlSession</code> 的操作。</p>
<p>SqlSession 的使用要么从线程的本地变量里去取，要么用完即弃。这样，每个线程使用到的都是”独属于自己“的SqlSession了。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.apache.ibatis.session;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SqlSessionManager</span> <span class="keyword">implements</span> <span class="title class_">SqlSessionFactory</span>, SqlSession &#123;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 从配置文件或者Java类生成的</span></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">final</span> SqlSessionFactory sqlSessionFactory;</span><br><span class="line">  <span class="comment">// 代理类</span></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">final</span> SqlSession sqlSessionProxy;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 线程本地变量</span></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">final</span> ThreadLocal&lt;SqlSession&gt; localSqlSession = <span class="keyword">new</span> <span class="title class_">ThreadLocal</span>&lt;&gt;();</span><br><span class="line"> </span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="title function_">SqlSessionManager</span><span class="params">(SqlSessionFactory sqlSessionFactory)</span> &#123;</span><br><span class="line">    <span class="built_in">this</span>.sqlSessionFactory = sqlSessionFactory;</span><br><span class="line">    <span class="comment">// Proxy.newProxyInstance</span></span><br><span class="line">    <span class="built_in">this</span>.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(</span><br><span class="line">        SqlSessionFactory.class.getClassLoader(),</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;SqlSession.class&#125;,</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">SqlSessionInterceptor</span>());</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 此方法可以直接绕过 SqlSessionFactoryBuilder的构造</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> SqlSessionManager <span class="title function_">newInstance</span><span class="params">(Reader reader)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">SqlSessionManager</span>(<span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBuilder</span>().build(reader, <span class="literal">null</span>, <span class="literal">null</span>));</span><br><span class="line">  &#125;</span><br><span class="line"> </span><br><span class="line">  <span class="comment">// 设置线程本地量</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">startManagedSession</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="built_in">this</span>.localSqlSession.set(openSession());</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 从 threadLocal里去取</span></span><br><span class="line">  <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isManagedSessionStarted</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.localSqlSession.get() != <span class="literal">null</span>;</span><br><span class="line">  &#125;</span><br><span class="line"> </span><br><span class="line">  <span class="comment">// 用代理对象调用，之后被 SqlSessionInterceptor 拦截</span></span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="keyword">public</span> &lt;T&gt; T <span class="title function_">selectOne</span><span class="params">(String statement)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> sqlSessionProxy.selectOne(statement);</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">class</span> <span class="title class_">SqlSessionInterceptor</span> <span class="keyword">implements</span> <span class="title class_">InvocationHandler</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">SqlSessionInterceptor</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// Prevent Synthetic Access</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 先从本地拿, 有则直接用, 无则直接用一个新的 session 执行任务</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">      <span class="keyword">final</span> <span class="type">SqlSession</span> <span class="variable">sqlSession</span> <span class="operator">=</span> SqlSessionManager.<span class="built_in">this</span>.localSqlSession.get();</span><br><span class="line">      <span class="comment">// </span></span><br><span class="line">      <span class="keyword">if</span> (sqlSession != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">          <span class="keyword">return</span> method.invoke(sqlSession, args);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Throwable t) &#123;</span><br><span class="line">          <span class="keyword">throw</span> ExceptionUtil.unwrapThrowable(t);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> (<span class="type">SqlSession</span> <span class="variable">autoSqlSession</span> <span class="operator">=</span> openSession()) &#123;</span><br><span class="line">          <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">final</span> <span class="type">Object</span> <span class="variable">result</span> <span class="operator">=</span> method.invoke(autoSqlSession, args);</span><br><span class="line">            autoSqlSession.commit();</span><br><span class="line">            <span class="keyword">return</span> result;</span><br><span class="line">          &#125; <span class="keyword">catch</span> (Throwable t) &#123;</span><br><span class="line">            autoSqlSession.rollback();</span><br><span class="line">            <span class="keyword">throw</span> ExceptionUtil.unwrapThrowable(t);</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="SqlSessionTemplate"><a href="#SqlSessionTemplate" class="headerlink" title="SqlSessionTemplate"></a>SqlSessionTemplate</h2><p>这个类在 org.mybatis.spring包中，也实现了 SqlSession接口，也是线程安全的。</p>
<p>不过，这个类在获得目标对象 SqlSession 的时候，并不是简单的从ThreadLocal里取，而是借助 <code>TransactionSynchronizationManager</code>去获取<code>SqlSessionHolder</code>再最终获得 <code>SqlSession</code>的。</p>
<p>这里不再赘述，放在spring 的事务管理里细述。</p>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://mybatis.org/mybatis-3/zh/getting-started.html">MyBatis 3 文档</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/08/05/ThreadLocal%E4%BD%BF%E7%94%A8%E3%80%81%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/08/05/ThreadLocal%E4%BD%BF%E7%94%A8%E3%80%81%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/" class="post-title-link" itemprop="url">ThreadLocal使用、源码解析</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-08-05 00:00:00" itemprop="dateCreated datePublished" datetime="2021-08-05T00:00:00+08:00">2021-08-05</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-01-05 02:57:48" itemprop="dateModified" datetime="2023-01-05T02:57:48+08:00">2023-01-05</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Java%E5%A4%9A%E7%BA%BF%E7%A8%8B/" itemprop="url" rel="index"><span itemprop="name">Java多线程</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="ThreadLocal的作用"><a href="#ThreadLocal的作用" class="headerlink" title="ThreadLocal的作用"></a>ThreadLocal的作用</h1><ol>
<li>方便传参（参考反面例子，go没有适用于goroutine的threadlocal，只能显式函数传参）</li>
<li>资源隔离（数据库连接）</li>
</ol>
<h1 id="使用ThreadLocal线程ID工具类"><a href="#使用ThreadLocal线程ID工具类" class="headerlink" title="使用ThreadLocal线程ID工具类"></a>使用ThreadLocal线程ID工具类</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.concurrent.atomic.AtomicInteger;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">实现了一个线程ID工具类</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">-----来自源码文档demo</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadId</span> &#123;</span><br><span class="line">    <span class="comment">// Atomic integer containing the next thread ID to be assigned</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">AtomicInteger</span> <span class="variable">nextId</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicInteger</span>(<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Thread local variable containing each thread&#x27;s ID</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> ThreadLocal&lt;Integer&gt; threadId =</span><br><span class="line">            ThreadLocal.withInitial(nextId::getAndIncrement);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Returns the current thread&#x27;s unique ID, assigning it if necessary</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">get</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> threadId.get();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;&#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;A: &quot;</span>+ThreadId.get());</span><br><span class="line">            System.out.println(<span class="string">&quot;A: &quot;</span>+ThreadId.get());</span><br><span class="line">        &#125;).start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;&#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;B: &quot;</span>+ThreadId.get());</span><br><span class="line">            System.out.println(<span class="string">&quot;B: &quot;</span>+ThreadId.get());</span><br><span class="line">        &#125;).start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;&#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;C: &quot;</span>+ThreadId.get());</span><br><span class="line">            System.out.println(<span class="string">&quot;C: &quot;</span>+ThreadId.get());</span><br><span class="line">        &#125;).start();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h1 id="源码分析"><a href="#源码分析" class="headerlink" title="源码分析"></a>源码分析</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> java.lang;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadLocal</span>&lt;T&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 此ThreadLocal对象的“自定义”的哈希吗</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">threadLocalHashCode</span> <span class="operator">=</span> nextHashCode();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 静态的线程安全的循环计数器</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">AtomicInteger</span> <span class="variable">nextHashCode</span> <span class="operator">=</span></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">AtomicInteger</span>();</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 连续的 ThreadLocal的哈希值的增量差异（此 magic number 可以产生离散程度很好的哈希表）</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">HASH_INCREMENT</span> <span class="operator">=</span> <span class="number">0x61c88647</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取下一个 hash 值的工具方法</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">nextHashCode</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> nextHashCode.getAndAdd(HASH_INCREMENT);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回当前线程获取此 thread-local 对象的初始值</span></span><br><span class="line">    <span class="comment">// 可以用匿名内部类（lambda表达式代替）覆盖此方法，以提供初始值</span></span><br><span class="line">    <span class="keyword">protected</span> T <span class="title function_">initialValue</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ThreadLocal</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回此线程局部变量的当前线程中的值</span></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">    	步骤：</span></span><br><span class="line"><span class="comment">    	1、获取当前线程</span></span><br><span class="line"><span class="comment">    	2、获取此线程拥有的 ThreadLocalMap</span></span><br><span class="line"><span class="comment">    	3、在 ThreadLocalMap 中根据此 ThreadLocal 对象拿到值</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    <span class="keyword">public</span> T <span class="title function_">get</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t</span> <span class="operator">=</span> Thread.currentThread();</span><br><span class="line">        <span class="type">ThreadLocalMap</span> <span class="variable">map</span> <span class="operator">=</span> getMap(t);</span><br><span class="line">        <span class="keyword">if</span> (map != <span class="literal">null</span>) &#123;</span><br><span class="line">            ThreadLocalMap.<span class="type">Entry</span> <span class="variable">e</span> <span class="operator">=</span> map.getEntry(<span class="built_in">this</span>);</span><br><span class="line">            <span class="keyword">if</span> (e != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span></span><br><span class="line">                <span class="type">T</span> <span class="variable">result</span> <span class="operator">=</span> (T)e.value;</span><br><span class="line">                <span class="keyword">return</span> result;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> setInitialValue();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    </span><br><span class="line">  	<span class="comment">/*</span></span><br><span class="line"><span class="comment">    	步骤：</span></span><br><span class="line"><span class="comment">    	1、获取当前线程</span></span><br><span class="line"><span class="comment">    	2、获取此线程拥有的 ThreadLocalMap</span></span><br><span class="line"><span class="comment">    	3、调用map 的 setter 方法（ 如果未初始化，则先初始化 ）</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">set</span><span class="params">(T value)</span> &#123;</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t</span> <span class="operator">=</span> Thread.currentThread();</span><br><span class="line">        <span class="type">ThreadLocalMap</span> <span class="variable">map</span> <span class="operator">=</span> getMap(t);</span><br><span class="line">        <span class="keyword">if</span> (map != <span class="literal">null</span>) &#123;</span><br><span class="line">            map.set(<span class="built_in">this</span>, value);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 创建此线程的自己的 ThreadLocalMap</span></span><br><span class="line">            createMap(t, value);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">     <span class="comment">// ThreadLocal 允许线程”删除“它的线程本地量</span></span><br><span class="line">     <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">remove</span><span class="params">()</span> &#123;</span><br><span class="line">         <span class="type">ThreadLocalMap</span> <span class="variable">m</span> <span class="operator">=</span> getMap(Thread.currentThread());</span><br><span class="line">         <span class="keyword">if</span> (m != <span class="literal">null</span>) &#123;</span><br><span class="line">             m.remove(<span class="built_in">this</span>);</span><br><span class="line">         &#125;</span><br><span class="line">     &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 每个线程有自己的 ThreadLocalMap实例，用 Thread 类的 threadLocals 字段设置</span></span><br><span class="line">    <span class="comment">// 然后在某个 ThreadLocalMap实例 中根据 threadLocal实例 取值</span></span><br><span class="line">    ThreadLocalMap <span class="title function_">getMap</span><span class="params">(Thread t)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> t.threadLocals;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 设置线程的threadLocals</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">createMap</span><span class="params">(Thread t, T firstValue)</span> &#123;</span><br><span class="line">        t.threadLocals = <span class="keyword">new</span> <span class="title class_">ThreadLocalMap</span>(<span class="built_in">this</span>, firstValue);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="ThreadLocal的内部类-ThreadLocalMap"><a href="#ThreadLocal的内部类-ThreadLocalMap" class="headerlink" title="ThreadLocal的内部类 ThreadLocalMap"></a>ThreadLocal的内部类 ThreadLocalMap</h2><blockquote>
<p>ThreadLocalMap 是一种定制的哈希映射，仅适用于维护线程本地值。</p>
</blockquote>
<p>每个线程有自己的 ThreadLocalMap实例，用 Thread 类的 threadLocals 字段设置。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">ThreadLocalMap</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 注意这里的弱引用 */</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Entry</span> <span class="keyword">extends</span> <span class="title class_">WeakReference</span>&lt;ThreadLocal&lt;?&gt;&gt; &#123;</span><br><span class="line">        <span class="comment">/** The value associated with this ThreadLocal. */</span></span><br><span class="line">        Object value;</span><br><span class="line"></span><br><span class="line">        Entry(ThreadLocal&lt;?&gt; k, Object v) &#123;</span><br><span class="line">            <span class="built_in">super</span>(k);</span><br><span class="line">            value = v;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// table 的默认容量</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">INITIAL_CAPACITY</span> <span class="operator">=</span> <span class="number">16</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 有需要就扩容，容量大小为 2 的整数次幂</span></span><br><span class="line">    <span class="keyword">private</span> Entry[] table;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * The number of entries in the table.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 下一次进行 resize 的阈值</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> threshold; <span class="comment">// Default to 0</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 负载因子为 2/3 </span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">setThreshold</span><span class="params">(<span class="type">int</span> len)</span> &#123;</span><br><span class="line">        threshold = len * <span class="number">2</span> / <span class="number">3</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 取模意义下的 ”后一个位置“</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">nextIndex</span><span class="params">(<span class="type">int</span> i, <span class="type">int</span> len)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> ((i + <span class="number">1</span> &lt; len) ? i + <span class="number">1</span> : <span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 取模意义下的 ”前一个位置“</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">prevIndex</span><span class="params">(<span class="type">int</span> i, <span class="type">int</span> len)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> ((i - <span class="number">1</span> &gt;= <span class="number">0</span>) ? i - <span class="number">1</span> : len - <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 初始化惰性构造</span></span><br><span class="line">    ThreadLocalMap(ThreadLocal&lt;?&gt; firstKey, Object firstValue) &#123;</span><br><span class="line">        table = <span class="keyword">new</span> <span class="title class_">Entry</span>[INITIAL_CAPACITY];</span><br><span class="line">        <span class="comment">// 一样的技巧 —— 位运算代替取模</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> firstKey.threadLocalHashCode &amp; (INITIAL_CAPACITY - <span class="number">1</span>);</span><br><span class="line">        table[i] = <span class="keyword">new</span> <span class="title class_">Entry</span>(firstKey, firstValue);</span><br><span class="line">        size = <span class="number">1</span>;</span><br><span class="line">        setThreshold(INITIAL_CAPACITY);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Entry <span class="title function_">getEntry</span><span class="params">(ThreadLocal&lt;?&gt; key)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> key.threadLocalHashCode &amp; (table.length - <span class="number">1</span>);</span><br><span class="line">        <span class="type">Entry</span> <span class="variable">e</span> <span class="operator">=</span> table[i];</span><br><span class="line">        <span class="keyword">if</span> (e != <span class="literal">null</span> &amp;&amp; e.get() == key)</span><br><span class="line">            <span class="keyword">return</span> e;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="keyword">return</span> getEntryAfterMiss(key, i, e);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h1 id="内存泄漏分析demo"><a href="#内存泄漏分析demo" class="headerlink" title="内存泄漏分析demo"></a>内存泄漏分析demo</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Node</span> &#123;</span><br><span class="line">        <span class="type">byte</span>[] bytes = <span class="keyword">new</span> <span class="title class_">byte</span>[<span class="number">1024</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>)&#123;</span><br><span class="line">            ThreadLocal&lt;Node&gt; threadLocal = <span class="keyword">new</span> <span class="title class_">ThreadLocal</span>&lt;&gt;();</span><br><span class="line">            <span class="type">Node</span> <span class="variable">node</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>()</span><br><span class="line">            threadLocal.set(<span class="keyword">new</span> <span class="title class_">Node</span>());</span><br><span class="line"><span class="comment">//            System.out.println(threadLocal.get());</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>引用链分析</strong>:</p>
<ul>
<li>GC对象Node:<br>局部变量引用node，</li>
</ul>
<p>另一条引用链</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Thread.currentThread()-&gt;threadLocals(ThreadLocalMap)-&gt;table(Entry[])-&gt;Entry--&gt;threadLocal</span><br></pre></td></tr></table></figure>
<p>最后一个是弱引用</p>
<ul>
<li>GC对象ThreadLocal<br>局部变量引用 threadLocal，</li>
</ul>
<p>另一条引用链</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Thread.currentThread()-&gt;threadLocals(ThreadLocalMap)-&gt;table(Entry[])-&gt;Entry-&gt;node</span><br></pre></td></tr></table></figure>
<p>都是强引用。</p>
<blockquote>
<p>(Weakly Reachable )[<a target="_blank" rel="noopener" href="https://www.cs.fsu.edu/~jtbauer/cis3931/tutorial/refobjs/about/weak.html]">https://www.cs.fsu.edu/~jtbauer/cis3931/tutorial/refobjs/about/weak.html]</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/08/03/%E8%BF%99%E6%AC%A1%E5%BD%BB%E5%BA%95%E8%A7%A3%E5%86%B3HashMap!/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/08/03/%E8%BF%99%E6%AC%A1%E5%BD%BB%E5%BA%95%E8%A7%A3%E5%86%B3HashMap!/" class="post-title-link" itemprop="url">这次彻底解决HashMap!</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-08-03 00:00:00" itemprop="dateCreated datePublished" datetime="2021-08-03T00:00:00+08:00">2021-08-03</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-12-12 16:20:43" itemprop="dateModified" datetime="2021-12-12T16:20:43+08:00">2021-12-12</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Java%E9%9B%86%E5%90%88%E7%B1%BB/" itemprop="url" rel="index"><span itemprop="name">Java集合类</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <blockquote>
<p>前言，这篇文章着眼于JDK中HashMap的实现，源码解析，以及常见的面试问题。</p>
<p>对于一般的哈希表的实现，各种散列算法以及解决哈希冲突的方案本文并不完全介绍。</p>
<p>而且，关于红黑树的部分，本篇文章也不设计，只是直接调用api。</p>
</blockquote>
<h1 id="HashMap-文档"><a href="#HashMap-文档" class="headerlink" title="HashMap 文档"></a>HashMap 文档</h1><p>以下来自 HashMap 文档。</p>
<p>重点已加粗，并且增加了自己的批注。</p>
<blockquote>
<p>基于哈希表的Map接口的实现。</p>
<p>此实现提供所有可选的映射操作，并<strong>允许空值和空键</strong>。 </p>
<p>HashMap类与Hashtable底层都是hash表，不同之处在于<strong>HashMap线程不安全</strong>，并且允许 null。</p>
<p>该实现为<strong>基本操作（ get和put ）提供了常量时间的性能</strong>，假设哈希函数将元素正确地分散在存储桶中。 </p>
<p>集合视图上的迭代所需的时间与HashMap实例的“capacity”（存储桶数）及其 size（键-值映射数）成正比。 因此，如果迭代性能很重要，则不要将初始容量设置得过高（或负载因子过低），这一点非常重要。（<strong>否则会产生大量空桶,影响遍历效率</strong>）</p>
<p>HashMap的实例具有两个影响其性能的参数：<strong>初始容量（ initial capacity ）和负载因子（ load factor）</strong>。</p>
<p><strong>容量是哈希表中存储桶的数量</strong>，初始容量是创建哈希表时的容量。 </p>
<p><strong>负载因子是散列表的容量自动增加之前允许其填充的完整程度的度量</strong>。 当哈希表中的条目数超过<strong>负载因子和当前容量的乘积</strong>时，哈希表将被重新哈希（rehashed）（即内部数据结构将被重建）。</p>
<p>通常，<strong>默认负载因子（0.75）</strong>在时间和空间成本之间提供了一个很好的折衷方案。 较高的值会减少空间开销，但会增加查找成本（在HashMap类的大多数操作中都得到了体现，包括get和put ）。 设置其初始容量时，应考虑映射中的预期条目数及其负载因子，以最大程度地减少重新哈希操作的次数。 如果初始容量大于最大条目数除以负载因子，则将不会进行任何哈希操作。</p>
<p>如果将许多映射存储在HashMap实例中，则创建具有足够大容量的映射将比让其根据需要增长表的自动重新哈希处理更有效地存储映射（<strong>即如果确定在创建时就有大量映射存入HashMap,为了避免反复扩容，应确保足够大的初始容量</strong>）。 请注意，在具有大量相同hashCode()的键会降低哈希表性能， 为了改善影响，<strong>当键为Comparable</strong> ，此类可以使用键之间的比较顺序来帮助打破平局（<strong>即链表转红黑树</strong>）。</p>
<p>请注意，HashMap未实现同步。 如果多个线程同时访问一个哈希映射，并且至少有一个线程在结构上修改该映射，则必须在外部进行同步。 （<strong>结构修改是添加或删除一个或多个映射的任何操作；仅更改已经包含的键相关联的值不是结构修改</strong>。）</p>
</blockquote>
<h1 id="HashMap"><a href="#HashMap" class="headerlink" title="HashMap"></a>HashMap</h1><h2 id="数据组织结构"><a href="#数据组织结构" class="headerlink" title="数据组织结构"></a>数据组织结构</h2><p><strong>数组+链表+红黑树—— 一般的哈希表 + 红黑树</strong></p>
<p>如下图：</p>
<p><img src="/2021/08/03/%E8%BF%99%E6%AC%A1%E5%BD%BB%E5%BA%95%E8%A7%A3%E5%86%B3HashMap!/hashmap1.png" alt="哈希表结构"></p>
<h2 id="HashMap的核心参数"><a href="#HashMap的核心参数" class="headerlink" title="HashMap的核心参数"></a>HashMap的核心参数</h2><ul>
<li><p><code>capacity</code>：HashMap的容量，即Node[]数组的size，HashMap会强制的将容量设置为2的幂，这样在对key取hash值的时候可以直接使用位运算来代替一般的取模。（比如容量为16时，只取key对象hash值的后4位，其值肯定在0~15的范围内，不会造成数组越界）</p>
</li>
<li><p><code>initialCapacity</code>：初始容量（默认为16），在使用HashMap的构造器实例化对象的时候，可以根据数据量设置map的初始化容量，来避免HashMap的频繁扩容</p>
</li>
<li><p><code>MAXIMUM_CAPACITY</code>：HashMap的最大容量（常量：2^30），当HashMap的容量达到MAXIMUM_CAPACITY后，将不再会扩容</p>
</li>
<li><p><code>loadFactor</code>：加载因子（默认为0.75），用来确定什么时候需要扩容。用于避免Node[]数组中有过多的链表或树形结构。加载因子越大，空间利用率越高，但是数组中的链表结构可能就越长，查找效率越小。下图是关于加载因子大小的分析。</p>
</li>
</ul>
<p><img src="/2021/08/03/%E8%BF%99%E6%AC%A1%E5%BD%BB%E5%BA%95%E8%A7%A3%E5%86%B3HashMap!/load-factor.png" alt="a"></p>
<ul>
<li><p><code>threshold</code>：阈值（默认为16*0.75），其值等于capacity×loadFactor，当HashMap中的键值对数量达到这个阈值时，就需要扩容了（JDK1.7还会判断当前的数组位置是否为空）</p>
</li>
<li><p><code>TREEIFY_THRESHOLD</code>：桶的树化阈值（常量，值为8），即链表转为红黑树的阈值，当某一链表的长度<strong>大于</strong>该值时，就考虑转化成红黑树了（还需要考虑最小树形化容量阈值MIN_TREEIFY_CAPACITY）</p>
</li>
<li><p><code>MIN_TREEIFY_CAPACITY</code>：最小树形化容量阈值（常量，值为64），当某一链表的长度大于8（桶的树化阈值）时，如果此时HashMap的容量不大于64，则此时不会将链表转化为红黑树，而是对HashMap进行扩容，只有容量大于64时，才对链表转化成红黑树。</p>
</li>
<li><p><code>UNTREEIFY_THRESHOLD</code>：桶的链表还原阈值（常量，值为6），即红黑树转化成链表的阈值，当在扩容（resize()）时，HashMap中数据的储存位置会发生改变，在重新计算过储存位置后，如果红黑树中元素的数量小于6，那么将会把红黑树结构转化成链表结构。</p>
</li>
</ul>
<h2 id="构造器"><a href="#构造器" class="headerlink" title="构造器"></a>构造器</h2><p>如下，为HashMap提供的三种构造器，值得注意的是无论哪一种，都没有初始化数组，都只会在第一次添加元素的时候，惰性地去将<code>table数组</code>扩容，也就是一个空的HashMap没有浪费太多空间。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 可以指定初始容量、负载因子</span></span><br><span class="line"><span class="keyword">public</span> <span class="title function_">HashMap</span><span class="params">(<span class="type">int</span> initialCapacity, <span class="type">float</span> loadFactor)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (initialCapacity &lt; <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;Illegal initial capacity: &quot;</span> +</span><br><span class="line">                                           initialCapacity);</span><br><span class="line">    <span class="keyword">if</span> (initialCapacity &gt; MAXIMUM_CAPACITY)</span><br><span class="line">        initialCapacity = MAXIMUM_CAPACITY;</span><br><span class="line">    <span class="keyword">if</span> (loadFactor &lt;= <span class="number">0</span> || Float.isNaN(loadFactor))</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;Illegal load factor: &quot;</span> +</span><br><span class="line">                                           loadFactor);</span><br><span class="line">    <span class="built_in">this</span>.loadFactor = loadFactor;</span><br><span class="line">    <span class="built_in">this</span>.threshold = tableSizeFor(initialCapacity); <span class="comment">// 大于等于 initialCapacity 的最小2的整数次幂</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="title function_">HashMap</span><span class="params">(<span class="type">int</span> initialCapacity)</span> &#123;</span><br><span class="line">    <span class="built_in">this</span>(initialCapacity, DEFAULT_LOAD_FACTOR);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 注意此时 threshold = 0， 表示下次扩容时应扩展至 DEFAULT_INITIAL_CAPACITY </span></span><br><span class="line"><span class="keyword">public</span> <span class="title function_">HashMap</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="built_in">this</span>.loadFactor = DEFAULT_LOAD_FACTOR; <span class="comment">// all other fields defaulted</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="put-操作"><a href="#put-操作" class="headerlink" title="put 操作"></a>put 操作</h2><p>根据源码的put操作即可绘制下面的流程图：</p>
<p><img src="/2021/08/03/%E8%BF%99%E6%AC%A1%E5%BD%BB%E5%BA%95%E8%A7%A3%E5%86%B3HashMap!/HashMap-put.png" alt="put"></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// onlyIfAbsent 的作用是为 putIfAbsent等 API提供服务</span></span><br><span class="line"><span class="comment">// 调用了 newNode()、resize()、putTreeVal()、treeifyBin()</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// afterNodeAccess()、afterNodeInsertion （LinkedMap的性质维护）</span></span><br><span class="line"><span class="keyword">final</span> V <span class="title function_">putVal</span><span class="params">(<span class="type">int</span> hash, K key, V value, <span class="type">boolean</span> onlyIfAbsent,</span></span><br><span class="line"><span class="params">               <span class="type">boolean</span> evict)</span> &#123;</span><br><span class="line">    Node&lt;K,V&gt;[] tab; Node&lt;K,V&gt; p; <span class="type">int</span> n, i;</span><br><span class="line">  </span><br><span class="line">    <span class="comment">// 1. tab 为空时，需要初始化一下</span></span><br><span class="line">    <span class="keyword">if</span> ((tab = table) == <span class="literal">null</span> || (n = tab.length) == <span class="number">0</span>)</span><br><span class="line">        n = (tab = resize()).length;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> ((p = tab[i = (n - <span class="number">1</span>) &amp; hash]) == <span class="literal">null</span>)</span><br><span class="line">        <span class="comment">// 2.1 桶现在是空</span></span><br><span class="line">        tab[i] = newNode(hash, key, value, <span class="literal">null</span>);</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        Node&lt;K,V&gt; e; K k;</span><br><span class="line">        <span class="comment">// 判断第一个节点的性质———— 1.key相同（key相同的前提是hash相等）2. rb树的根， 3. 链表的表头</span></span><br><span class="line">        <span class="comment">// 2.2.1 找到一个 key 对应的节点</span></span><br><span class="line">        <span class="keyword">if</span> (p.hash == hash &amp;&amp;</span><br><span class="line">            ((k = p.key) == key || (key != <span class="literal">null</span> &amp;&amp; key.equals(k))))</span><br><span class="line">            e = p;</span><br><span class="line">        <span class="comment">// 2.2.2 已经树化了，p 此时为 树根，调用它的 putTreeVal </span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (p <span class="keyword">instanceof</span> TreeNode)</span><br><span class="line">            e = ((TreeNode&lt;K,V&gt;)p).putTreeVal(<span class="built_in">this</span>, tab, hash, key, value);</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 2.2.3 遍历链表</span></span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">binCount</span> <span class="operator">=</span> <span class="number">0</span>; ; ++binCount) &#123;</span><br><span class="line">                <span class="comment">//整体逻辑是 先判空，后判相等</span></span><br><span class="line">              </span><br><span class="line">                <span class="keyword">if</span> ((e = p.next) == <span class="literal">null</span>) &#123;</span><br><span class="line">                    <span class="comment">// 链表走到头了</span></span><br><span class="line">                    p.next = newNode(hash, key, value, <span class="literal">null</span>);</span><br><span class="line">                    <span class="comment">// 检查是否可以树化</span></span><br><span class="line">                    <span class="comment">// treeifyBin 里面会验证是否超过了 MIN_TREEIFY_CAPACITY</span></span><br><span class="line">                    <span class="keyword">if</span> (binCount &gt;= TREEIFY_THRESHOLD - <span class="number">1</span>) <span class="comment">// -1 for 1st</span></span><br><span class="line">                        treeifyBin(tab, hash);</span><br><span class="line">                    <span class="comment">// 进入这个逻辑即要退出循环</span></span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">// 和上面的key的判定是一样的</span></span><br><span class="line">                <span class="keyword">if</span> (e.hash == hash &amp;&amp;</span><br><span class="line">                    ((k = e.key) == key || (key != <span class="literal">null</span> &amp;&amp; key.equals(k))))</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                <span class="comment">// 链表节点后移</span></span><br><span class="line">                p = e;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 已经存在这个映射，用新值代替旧值</span></span><br><span class="line">        <span class="keyword">if</span> (e != <span class="literal">null</span>) &#123; <span class="comment">// existing mapping for key</span></span><br><span class="line">            <span class="type">V</span> <span class="variable">oldValue</span> <span class="operator">=</span> e.value;</span><br><span class="line">            <span class="keyword">if</span> (!onlyIfAbsent || oldValue == <span class="literal">null</span>)</span><br><span class="line">                e.value = value; <span class="comment">// 替换旧值</span></span><br><span class="line">            <span class="comment">// ...</span></span><br><span class="line">            afterNodeAccess(e);</span><br><span class="line">            <span class="keyword">return</span> oldValue; <span class="comment">// 返回旧值</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ++modCount; <span class="comment">// fast-fail 机制</span></span><br><span class="line">  </span><br><span class="line">    <span class="comment">// 先插入后检查是否要扩容</span></span><br><span class="line">    <span class="keyword">if</span> (++size &gt; threshold)</span><br><span class="line">        <span class="comment">// 调用扩容方法</span></span><br><span class="line">        resize();</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    afterNodeInsertion(evict);</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="get-操作"><a href="#get-操作" class="headerlink" title="get 操作"></a>get 操作</h2><p>get操作比较简单。</p>
<p>在数组不为空的情况下，hash值与数组长度取模（实际上是位于运算）定位到桶的位置。</p>
<p>一样的，先检查桶中的第一个节点，如果key匹配，返回。</p>
<p>如果第一个不匹配，要么在树中搜寻，要么在链表中遍历（根据节点的 instance 判断）。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 传入 key 的哈希吗以及key， 且 hash = hash(key)</span></span><br><span class="line"><span class="keyword">final</span> Node&lt;K,V&gt; <span class="title function_">getNode</span><span class="params">(<span class="type">int</span> hash, Object key)</span> &#123;</span><br><span class="line">    Node&lt;K,V&gt;[] tab; Node&lt;K,V&gt; first, e; <span class="type">int</span> n; K k;</span><br><span class="line">    <span class="keyword">if</span> ((tab = table) != <span class="literal">null</span> &amp;&amp; (n = tab.length) &gt; <span class="number">0</span> &amp;&amp;</span><br><span class="line">        (first = tab[(n - <span class="number">1</span>) &amp; hash]) != <span class="literal">null</span>) &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 总是先检查第一个节点，因为第一个节点既有可能是链表节点，也有可能是红黑树的树根</span></span><br><span class="line">        <span class="keyword">if</span> (first.hash == hash &amp;&amp; <span class="comment">// always check first node</span></span><br><span class="line">            <span class="comment">// 注意这里的 k 是可能为 null 的（HashMap允许为空）</span></span><br><span class="line">            ((k = first.key) == key || (key != <span class="literal">null</span> &amp;&amp; key.equals(k))))</span><br><span class="line">            <span class="keyword">return</span> first;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> ((e = first.next) != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 如果Node已经树化，则调用树的 getTreeNode 的 方法</span></span><br><span class="line">            <span class="keyword">if</span> (first <span class="keyword">instanceof</span> TreeNode)</span><br><span class="line">                <span class="keyword">return</span> ((TreeNode&lt;K,V&gt;)first).getTreeNode(hash, key);</span><br><span class="line">            <span class="keyword">do</span> &#123;</span><br><span class="line">                <span class="comment">// 判断顺序 hash（最快） -&gt; key的==比较 -&gt; key 的equals比较</span></span><br><span class="line">                <span class="keyword">if</span> (e.hash == hash &amp;&amp;</span><br><span class="line">                    ((k = e.key) == key || (key != <span class="literal">null</span> &amp;&amp; key.equals(k))))</span><br><span class="line">                    <span class="keyword">return</span> e;</span><br><span class="line">            &#125; <span class="keyword">while</span> ((e = e.next) != <span class="literal">null</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="resize（扩容操作）"><a href="#resize（扩容操作）" class="headerlink" title="resize（扩容操作）"></a>resize（扩容操作）</h2><blockquote>
<p>jdk 1.8 以来，rehash 的过程也进行了改动，基于复制的算法思想,不直接操作原链，而是定义了两条链表分别完成对原链的结点分离操作，即使是多线程的情况下也是安全的。</p>
</blockquote>
<p>扩容操作分为两个部分：</p>
<h3 id="确定新的容量和阈值"><a href="#确定新的容量和阈值" class="headerlink" title="确定新的容量和阈值"></a>确定新的容量和阈值</h3><p>具体的：</p>
<ol>
<li><p>如果调用的是无参构造参数，在第一次扩容时，容量默认是16，扩容阈值为12（16*0.75）</p>
</li>
<li><p>如果调用的是有参构造函数，在第一次扩容时，容量为大于等于<code>initialCapacity</code>的最小二次幂，</p>
<p>阈值为容量*负载因子。</p>
</li>
<li><p>其他情况下，如果旧容量已经大于<code>MAXIMUM_CAPACITY</code>不选择扩容，直接返回；否则容量double一下, 阈值为容量*负载因子。</p>
</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 初始化或double容量</span></span><br><span class="line"><span class="comment">// 由于在capacity为2的整数次幂的情况下，取模具有这样的特点：</span></span><br><span class="line"><span class="comment">// 某个Node在扩容之后要么在table[index](aka 原位置)，要么在table[index + oldCap]</span></span><br><span class="line"><span class="keyword">final</span> Node&lt;K,V&gt;[] resize() &#123;</span><br><span class="line">    Node&lt;K,V&gt;[] oldTab = table;</span><br><span class="line">    <span class="type">int</span> <span class="variable">oldCap</span> <span class="operator">=</span> (oldTab == <span class="literal">null</span>) ? <span class="number">0</span> : oldTab.length;</span><br><span class="line">    <span class="type">int</span> <span class="variable">oldThr</span> <span class="operator">=</span> threshold;</span><br><span class="line">    <span class="type">int</span> newCap, newThr = <span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// old容量大于0</span></span><br><span class="line">    <span class="keyword">if</span> (oldCap &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="comment">// 原来的容量已经超过最大值， 此时无法扩容，直接返回</span></span><br><span class="line">        <span class="keyword">if</span> (oldCap &gt;= MAXIMUM_CAPACITY) &#123;</span><br><span class="line">            threshold = Integer.MAX_VALUE;</span><br><span class="line">            <span class="keyword">return</span> oldTab;</span><br><span class="line">        &#125;</span><br><span class="line">      </span><br><span class="line">        <span class="comment">// double old容量</span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> ((newCap = oldCap &lt;&lt; <span class="number">1</span>) &lt; MAXIMUM_CAPACITY &amp;&amp;  <span class="comment">// oldCap 最大为 (2**30-1)*2</span></span><br><span class="line">                 oldCap &gt;= DEFAULT_INITIAL_CAPACITY)</span><br><span class="line">            newThr = oldThr &lt;&lt; <span class="number">1</span>; <span class="comment">// double threshold</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// old容量等于0，并且old“下次调整值”大于0 （也就是说用户指定了initialCapacity）</span></span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (oldThr &gt; <span class="number">0</span>) <span class="comment">// 由用户初始化的容量（不过还要经过tableSizeFor方法）经 threshold 保存</span></span><br><span class="line">        newCap = oldThr;</span><br><span class="line">    <span class="comment">// old容量等于0，并且old“下次调整值”等于0</span></span><br><span class="line">    <span class="keyword">else</span> &#123;               <span class="comment">// 用户用无参构造器时的第一次初始化</span></span><br><span class="line">        newCap = DEFAULT_INITIAL_CAPACITY;</span><br><span class="line">        newThr = (<span class="type">int</span>)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);</span><br><span class="line">    &#125;</span><br><span class="line">  </span><br><span class="line">    <span class="keyword">if</span> (newThr == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="type">float</span> <span class="variable">ft</span> <span class="operator">=</span> (<span class="type">float</span>)newCap * loadFactor;</span><br><span class="line">        newThr = (newCap &lt; MAXIMUM_CAPACITY &amp;&amp; ft &lt; (<span class="type">float</span>)MAXIMUM_CAPACITY ?</span><br><span class="line">                  (<span class="type">int</span>)ft : Integer.MAX_VALUE); <span class="comment">// 赋值为 newCap * loadFactor， 除非超出最大值</span></span><br><span class="line">    &#125;</span><br><span class="line">    threshold = newThr; <span class="comment">// 更新 threshold </span></span><br><span class="line">  </span><br><span class="line">    <span class="meta">@SuppressWarnings(&#123;&quot;rawtypes&quot;,&quot;unchecked&quot;&#125;)</span></span><br><span class="line">    <span class="comment">// 分配内存，创建新数组</span></span><br><span class="line">    Node&lt;K,V&gt;[] newTab = (Node&lt;K,V&gt;[])<span class="keyword">new</span> <span class="title class_">Node</span>[newCap];</span><br><span class="line">    table = newTab;</span><br><span class="line">   </span><br><span class="line">    <span class="keyword">return</span> newTab;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="元素的再散列"><a href="#元素的再散列" class="headerlink" title="元素的再散列"></a>元素的再散列</h3><p>再散列的逻辑，</p>
<p>遍历旧数组的每一个桶，若桶为空，跳过；</p>
<p>若不为空，如果是树，调用TreeNode的split方法将一棵树分裂开来（如果树的节点个数小于<code>UNTREEIFY_THRESHOLD</code>）将树链表化；</p>
<p>如果是链表，在遍历这条链表的时候，构造两条链表，一条称为”低链表”，第二条称为“高链表”。</p>
<p>分别在遍历完链表后，一条放在原位置，一条放在（原位置+oldCap）的位置上。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 如果oldTab为null，也无需移动元素了</span></span><br><span class="line"><span class="keyword">if</span> (oldTab != <span class="literal">null</span>) &#123;</span><br><span class="line">    <span class="comment">// 遍历每一个桶</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; oldCap; ++j) &#123;</span><br><span class="line">        Node&lt;K,V&gt; e;</span><br><span class="line">        <span class="comment">// 链表为空就continue</span></span><br><span class="line">        <span class="keyword">if</span> ((e = oldTab[j]) != <span class="literal">null</span>) &#123;</span><br><span class="line">            oldTab[j] = <span class="literal">null</span>; <span class="comment">// help GC</span></span><br><span class="line">          </span><br><span class="line">            <span class="keyword">if</span> (e.next == <span class="literal">null</span>) <span class="comment">// 此时只有一个节点</span></span><br><span class="line">                newTab[e.hash &amp; (newCap - <span class="number">1</span>)] = e;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (e <span class="keyword">instanceof</span> TreeNode) <span class="comment">// 已经树化，则调用红黑树的方法，</span></span><br><span class="line">                <span class="comment">// 注意 split 方法中，可能将树链表化</span></span><br><span class="line">                ((TreeNode&lt;K,V&gt;)e).split(<span class="built_in">this</span>, newTab, j, oldCap);</span><br><span class="line">            <span class="keyword">else</span> &#123; <span class="comment">// 维护原顺序（“尾插法”）</span></span><br><span class="line">                </span><br><span class="line">                Node&lt;K,V&gt; loHead = <span class="literal">null</span>, loTail = <span class="literal">null</span>; <span class="comment">// table[index]对应的链表 </span></span><br><span class="line">                Node&lt;K,V&gt; hiHead = <span class="literal">null</span>, hiTail = <span class="literal">null</span>; <span class="comment">// table[index + oldLen]对应的链表</span></span><br><span class="line">                Node&lt;K,V&gt; next;</span><br><span class="line">                <span class="keyword">do</span> &#123;</span><br><span class="line">                    next = e.next;</span><br><span class="line">                    <span class="comment">// e 是当前要处理的元素</span></span><br><span class="line">                    <span class="keyword">if</span> ((e.hash &amp; oldCap) == <span class="number">0</span>) &#123; <span class="comment">// 注意此位运算的含义</span></span><br><span class="line">                        <span class="keyword">if</span> (loTail == <span class="literal">null</span>)</span><br><span class="line">                            loHead = e;</span><br><span class="line">                        <span class="keyword">else</span></span><br><span class="line">                            loTail.next = e;</span><br><span class="line">                        loTail = e;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">else</span> &#123;</span><br><span class="line">                        <span class="keyword">if</span> (hiTail == <span class="literal">null</span>)</span><br><span class="line">                            hiHead = e;</span><br><span class="line">                        <span class="keyword">else</span></span><br><span class="line">                            hiTail.next = e;</span><br><span class="line">                        hiTail = e;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">while</span> ((e = next) != <span class="literal">null</span>); <span class="comment">// 遍历链表</span></span><br><span class="line">              </span><br><span class="line">                <span class="comment">// 一条链经过resize后分裂成两条链</span></span><br><span class="line">                <span class="keyword">if</span> (loTail != <span class="literal">null</span>) &#123;</span><br><span class="line">                    loTail.next = <span class="literal">null</span>; <span class="comment">// 必须去除这个冗余的节点引用</span></span><br><span class="line">                    newTab[j] = loHead; </span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (hiTail != <span class="literal">null</span>) &#123;</span><br><span class="line">                    hiTail.next = <span class="literal">null</span>; <span class="comment">// 必须去除这个冗余的节点引用</span></span><br><span class="line">                    newTab[j + oldCap] = hiHead;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>







<h1 id="面试常见问题"><a href="#面试常见问题" class="headerlink" title="面试常见问题"></a>面试常见问题</h1><h2 id="JDK1-7与JDK1-8的区别"><a href="#JDK1-7与JDK1-8的区别" class="headerlink" title="JDK1.7与JDK1.8的区别"></a>JDK1.7与JDK1.8的区别</h2><h3 id="头插法改尾插法"><a href="#头插法改尾插法" class="headerlink" title="头插法改尾插法"></a>头插法改尾插法</h3><p>JDK1.7时的头插法的导致的死循环问题：</p>
<p><img src="/2021/08/03/%E8%BF%99%E6%AC%A1%E5%BD%BB%E5%BA%95%E8%A7%A3%E5%86%B3HashMap!/resize.png" alt="resize"></p>
<p>头插法改为尾插法：<br><img src="/2021/08/03/%E8%BF%99%E6%AC%A1%E5%BD%BB%E5%BA%95%E8%A7%A3%E5%86%B3HashMap!/dif-resize.png" alt="dif-resize"></p>
<h3 id="引入红黑树"><a href="#引入红黑树" class="headerlink" title="引入红黑树"></a>引入红黑树</h3><p><img src="/2021/08/03/%E8%BF%99%E6%AC%A1%E5%BD%BB%E5%BA%95%E8%A7%A3%E5%86%B3HashMap!/dif-put.png" alt="dif-put"></p>
<h1 id="源码解析"><a href="#源码解析" class="headerlink" title="源码解析"></a>源码解析</h1><h2 id="哈希表核心逻辑"><a href="#哈希表核心逻辑" class="headerlink" title="哈希表核心逻辑"></a>哈希表核心逻辑</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> java.util;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HashMap</span>&lt;K,V&gt; <span class="keyword">extends</span> <span class="title class_">AbstractMap</span>&lt;K,V&gt;</span><br><span class="line">    <span class="keyword">implements</span> <span class="title class_">Map</span>&lt;K,V&gt;, Cloneable, Serializable &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">serialVersionUID</span> <span class="operator">=</span> <span class="number">362498820763181265L</span>;</span><br><span class="line"></span><br><span class="line">   </span><br><span class="line">    <span class="comment">// 默认初始容量</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">DEFAULT_INITIAL_CAPACITY</span> <span class="operator">=</span> <span class="number">1</span> &lt;&lt; <span class="number">4</span>; <span class="comment">// aka 16</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//  最大容量</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">MAXIMUM_CAPACITY</span> <span class="operator">=</span> <span class="number">1</span> &lt;&lt; <span class="number">30</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 默认负载因子</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">float</span> <span class="variable">DEFAULT_LOAD_FACTOR</span> <span class="operator">=</span> <span class="number">0.75f</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 链表树化的最小阈值</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">TREEIFY_THRESHOLD</span> <span class="operator">=</span> <span class="number">8</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 红黑树链表化的最小阈值</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">UNTREEIFY_THRESHOLD</span> <span class="operator">=</span> <span class="number">6</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 树化的最小容量值（即如果容量小于 MIN_TREEIFY_CAPACITY ，即使链表长度大于 TREEIFY_THRESHOLD 也不会树化，这时会优先选择扩容）</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">MIN_TREEIFY_CAPACITY</span> <span class="operator">=</span> <span class="number">64</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 内部 Node</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Node</span>&lt;K,V&gt; <span class="keyword">implements</span> <span class="title class_">Map</span>.Entry&lt;K,V&gt; &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">int</span> hash;</span><br><span class="line">        <span class="keyword">final</span> K key;</span><br><span class="line">        V value;</span><br><span class="line">        <span class="comment">// 由于是链表结构, 所以有 next</span></span><br><span class="line">        Node&lt;K,V&gt; next;</span><br><span class="line"></span><br><span class="line">        Node(<span class="type">int</span> hash, K key, V value, Node&lt;K,V&gt; next) &#123;</span><br><span class="line">            <span class="built_in">this</span>.hash = hash;</span><br><span class="line">            <span class="built_in">this</span>.key = key;</span><br><span class="line">            <span class="built_in">this</span>.value = value;</span><br><span class="line">            <span class="built_in">this</span>.next = next;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">final</span> K <span class="title function_">getKey</span><span class="params">()</span>        &#123; <span class="keyword">return</span> key; &#125;</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">final</span> V <span class="title function_">getValue</span><span class="params">()</span>      &#123; <span class="keyword">return</span> value; &#125;</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">final</span> String <span class="title function_">toString</span><span class="params">()</span> &#123; <span class="keyword">return</span> key + <span class="string">&quot;=&quot;</span> + value; &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">int</span> <span class="title function_">hashCode</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> Objects.hashCode(key) ^ Objects.hashCode(value);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">final</span> V <span class="title function_">setValue</span><span class="params">(V newValue)</span> &#123;</span><br><span class="line">            <span class="type">V</span> <span class="variable">oldValue</span> <span class="operator">=</span> value;</span><br><span class="line">            value = newValue;</span><br><span class="line">            <span class="keyword">return</span> oldValue;</span><br><span class="line">        &#125;</span><br><span class="line">      </span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">equals</span><span class="params">(Object o)</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (o == <span class="built_in">this</span>)</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            <span class="keyword">if</span> (o <span class="keyword">instanceof</span> Map.Entry) &#123;</span><br><span class="line">                Map.Entry&lt;?,?&gt; e = (Map.Entry&lt;?,?&gt;)o;</span><br><span class="line">                <span class="keyword">if</span> (Objects.equals(key, e.getKey()) &amp;&amp;</span><br><span class="line">                    Objects.equals(value, e.getValue()))</span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* ---------------- Static utilities -------------- */</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 由于使用了长度为 2 的整数次幂的模长，为了避免hashCode高位的失效，</span></span><br><span class="line">    <span class="comment">// 通过让hashCode的高16位与低16位进行异或，达到尽可能使用hashCode的全部</span></span><br><span class="line">    <span class="comment">// 当然，如果hashCode已经分散的足够好，此方法的增益也没有多大</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="title function_">hash</span><span class="params">(Object key)</span> &#123;</span><br><span class="line">        <span class="type">int</span> h;</span><br><span class="line">        <span class="keyword">return</span> (key == <span class="literal">null</span>) ? <span class="number">0</span> : (h = key.hashCode()) ^ (h &gt;&gt;&gt; <span class="number">16</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">		<span class="comment">// 返回 x 的类，如果它是“类 C 实现 Comparable”的形式  &quot;，否则为空。</span></span><br><span class="line">    <span class="comment">// 但必须是直接实现了 Comparable 接口</span></span><br><span class="line">    <span class="keyword">static</span> Class&lt;?&gt; comparableClassFor(Object x) &#123;</span><br><span class="line">        <span class="keyword">if</span> (x <span class="keyword">instanceof</span> Comparable) &#123;</span><br><span class="line">            Class&lt;?&gt; c; Type[] ts, as; Type t; ParameterizedType p;</span><br><span class="line">          	<span class="comment">// String 使用的很频繁，所以直接先检查一下</span></span><br><span class="line">            <span class="keyword">if</span> ((c = x.getClass()) == String.class) <span class="comment">// bypass checks</span></span><br><span class="line">                <span class="keyword">return</span> c;</span><br><span class="line">            <span class="comment">// 返回表示由该对象表示的类或接口直接实现的接口的type</span></span><br><span class="line">            <span class="keyword">if</span> ((ts = c.getGenericInterfaces()) != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="comment">// 遍历它实现的接口</span></span><br><span class="line">                <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; ts.length; ++i) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (((t = ts[i]) <span class="keyword">instanceof</span> ParameterizedType) &amp;&amp;</span><br><span class="line">                        ((p = (ParameterizedType)t).getRawType() ==</span><br><span class="line">                         Comparable.class) &amp;&amp;</span><br><span class="line">                        (as = p.getActualTypeArguments()) != <span class="literal">null</span> &amp;&amp;</span><br><span class="line">                        as.length == <span class="number">1</span> &amp;&amp; as[<span class="number">0</span>] == c) <span class="comment">// type arg is c</span></span><br><span class="line">                        <span class="keyword">return</span> c;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Returns k.compareTo(x) if x matches kc (k&#x27;s screened comparable</span></span><br><span class="line"><span class="comment">     * class), else 0.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@SuppressWarnings(&#123;&quot;rawtypes&quot;,&quot;unchecked&quot;&#125;)</span> <span class="comment">// for cast to Comparable</span></span><br><span class="line">    <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">compareComparables</span><span class="params">(Class&lt;?&gt; kc, Object k, Object x)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> (x == <span class="literal">null</span> || x.getClass() != kc ? <span class="number">0</span> :</span><br><span class="line">                ((Comparable)k).compareTo(x));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 计算 大于等于 cap 的最小的2的整数次幂</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="title function_">tableSizeFor</span><span class="params">(<span class="type">int</span> cap)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> cap - <span class="number">1</span>;</span><br><span class="line">        n |= n &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">        n |= n &gt;&gt;&gt; <span class="number">2</span>;</span><br><span class="line">        n |= n &gt;&gt;&gt; <span class="number">4</span>;</span><br><span class="line">        n |= n &gt;&gt;&gt; <span class="number">8</span>;</span><br><span class="line">        n |= n &gt;&gt;&gt; <span class="number">16</span>;</span><br><span class="line">        <span class="keyword">return</span> (n &lt; <span class="number">0</span>) ? <span class="number">1</span> : (n &gt;= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* ---------------- Fields -------------- */</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 存储 Node 的数组，len 为 2^n</span></span><br><span class="line">    <span class="comment">// 此时取模运算 index = hash % len = hash &amp; (n - 1) </span></span><br><span class="line">    <span class="keyword">transient</span> Node&lt;K,V&gt;[] table;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 也是视图性质的”集合“</span></span><br><span class="line">    <span class="keyword">transient</span> Set&lt;Map.Entry&lt;K,V&gt;&gt; entrySet;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 键值对的数目</span></span><br><span class="line">    <span class="keyword">transient</span> <span class="type">int</span> size;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 修改的次数，支持 fast-fail 机制</span></span><br><span class="line">    <span class="keyword">transient</span> <span class="type">int</span> modCount;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 要调整大小的下一个大小值（容量 * 负载因子）cap*loadFactor 。 </span></span><br><span class="line">    <span class="type">int</span> threshold;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">final</span> <span class="type">float</span> loadFactor;</span><br><span class="line"></span><br><span class="line">  </span><br><span class="line">    <span class="comment">/* ---------------- Public operations -------------- */</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 构造方法见上</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">size</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size == <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> V <span class="title function_">get</span><span class="params">(Object key)</span> &#123;</span><br><span class="line">        Node&lt;K,V&gt; e;</span><br><span class="line">        <span class="keyword">return</span> (e = getNode(hash(key), key)) == <span class="literal">null</span> ? <span class="literal">null</span> : e.value;</span><br><span class="line">        <span class="comment">// 源码解析见上</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">containsKey</span><span class="params">(Object key)</span> &#123;</span><br><span class="line">        <span class="comment">// 有无 key 对应的 entry</span></span><br><span class="line">        <span class="keyword">return</span> getNode(hash(key), key) != <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> V <span class="title function_">put</span><span class="params">(K key, V value)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> putVal(hash(key), key, value, <span class="literal">false</span>, <span class="literal">true</span>);</span><br><span class="line">        <span class="comment">// 源码解析见上</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 链表树化方法</span></span><br><span class="line">    <span class="comment">// 调用 TreeNode.treeify()</span></span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">treeifyBin</span><span class="params">(Node&lt;K,V&gt;[] tab, <span class="type">int</span> hash)</span> &#123;</span><br><span class="line">        <span class="type">int</span> n, index; Node&lt;K,V&gt; e;</span><br><span class="line">        <span class="keyword">if</span> (tab == <span class="literal">null</span> || (n = tab.length) &lt; MIN_TREEIFY_CAPACITY) <span class="comment">// 如果table调小，优先扩容</span></span><br><span class="line">            resize();</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> ((e = tab[index = (n - <span class="number">1</span>) &amp; hash]) != <span class="literal">null</span>) &#123;</span><br><span class="line">            TreeNode&lt;K,V&gt; hd = <span class="literal">null</span>, tl = <span class="literal">null</span>; <span class="comment">// 根、前驱</span></span><br><span class="line">            <span class="keyword">do</span> &#123;</span><br><span class="line">                TreeNode&lt;K,V&gt; p = replacementTreeNode(e, <span class="literal">null</span>);</span><br><span class="line">                <span class="keyword">if</span> (tl == <span class="literal">null</span>)</span><br><span class="line">                    hd = p;</span><br><span class="line">                <span class="keyword">else</span> &#123;</span><br><span class="line">                    p.prev = tl;</span><br><span class="line">                    tl.next = p;</span><br><span class="line">                &#125;</span><br><span class="line">                tl = p;</span><br><span class="line">            &#125; <span class="keyword">while</span> ((e = e.next) != <span class="literal">null</span>);  <span class="comment">// 遍历链表</span></span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 树化</span></span><br><span class="line">            <span class="keyword">if</span> ((tab[index] = hd) != <span class="literal">null</span>)</span><br><span class="line">                hd.treeify(tab);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> V <span class="title function_">remove</span><span class="params">(Object key)</span> &#123;</span><br><span class="line">        Node&lt;K,V&gt; e;</span><br><span class="line">        <span class="keyword">return</span> (e = removeNode(hash(key), key, <span class="literal">null</span>, <span class="literal">false</span>, <span class="literal">true</span>)) == <span class="literal">null</span> ?</span><br><span class="line">            <span class="literal">null</span> : e.value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">  </span><br><span class="line">    <span class="comment">// 调用 getTreeNode()、removeTreeNode()</span></span><br><span class="line">    <span class="comment">// 调用回调方法 afterNodeRemoval()</span></span><br><span class="line">    <span class="comment">// matchValue、movable这两个控制变量主要是为了支持其他api方法</span></span><br><span class="line">    <span class="keyword">final</span> Node&lt;K,V&gt; <span class="title function_">removeNode</span><span class="params">(<span class="type">int</span> hash, Object key, Object value,</span></span><br><span class="line"><span class="params">                               <span class="type">boolean</span> matchValue, <span class="type">boolean</span> movable)</span> &#123;</span><br><span class="line">        Node&lt;K,V&gt;[] tab; Node&lt;K,V&gt; p; <span class="type">int</span> n, index;</span><br><span class="line">        <span class="keyword">if</span> ((tab = table) != <span class="literal">null</span> &amp;&amp; (n = tab.length) &gt; <span class="number">0</span> &amp;&amp;</span><br><span class="line">            (p = tab[index = (n - <span class="number">1</span>) &amp; hash]) != <span class="literal">null</span>) &#123;</span><br><span class="line">            Node&lt;K,V&gt; node = <span class="literal">null</span>, e; K k; V v;  <span class="comment">// node 维护为要删除的node</span></span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 找到 node</span></span><br><span class="line">            <span class="keyword">if</span> (p.hash == hash &amp;&amp;</span><br><span class="line">                ((k = p.key) == key || (key != <span class="literal">null</span> &amp;&amp; key.equals(k)))) <span class="comment">// 只有一个节点</span></span><br><span class="line">                node = p;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> ((e = p.next) != <span class="literal">null</span>) &#123; </span><br><span class="line">                <span class="keyword">if</span> (p <span class="keyword">instanceof</span> TreeNode)</span><br><span class="line">                    node = ((TreeNode&lt;K,V&gt;)p).getTreeNode(hash, key);  <span class="comment">// 调用树的getNode方法</span></span><br><span class="line">                 <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="keyword">do</span> &#123;</span><br><span class="line">                        <span class="keyword">if</span> (e.hash == hash &amp;&amp;</span><br><span class="line">                            ((k = e.key) == key ||</span><br><span class="line">                             (key != <span class="literal">null</span> &amp;&amp; key.equals(k)))) &#123;</span><br><span class="line">                            node = e;</span><br><span class="line">                            <span class="keyword">break</span>;</span><br><span class="line">                        &#125;</span><br><span class="line">                        p = e;</span><br><span class="line">                    &#125; <span class="keyword">while</span> ((e = e.next) != <span class="literal">null</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// node 是要删除的节点、p是前驱</span></span><br><span class="line">            <span class="keyword">if</span> (node != <span class="literal">null</span> &amp;&amp; (!matchValue || (v = node.value) == value ||</span><br><span class="line">                                 (value != <span class="literal">null</span> &amp;&amp; value.equals(v)))) &#123;</span><br><span class="line">                <span class="keyword">if</span> (node <span class="keyword">instanceof</span> TreeNode)</span><br><span class="line">                    ((TreeNode&lt;K,V&gt;)node).removeTreeNode(<span class="built_in">this</span>, tab, movable);</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (node == p) <span class="comment">// node 刚好是第一个节点</span></span><br><span class="line">                    tab[index] = node.next;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    p.next = node.next;</span><br><span class="line">                ++modCount;</span><br><span class="line">                --size;</span><br><span class="line">                afterNodeRemoval(node); <span class="comment">// 钩子函数善后</span></span><br><span class="line">                <span class="keyword">return</span> node;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 注意 capacity 并没有发生变化</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">clear</span><span class="params">()</span> &#123;</span><br><span class="line">        Node&lt;K,V&gt;[] tab;</span><br><span class="line">        modCount++;</span><br><span class="line">        <span class="keyword">if</span> ((tab = table) != <span class="literal">null</span> &amp;&amp; size &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            size = <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; tab.length; ++i)</span><br><span class="line">                tab[i] = <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 此时间复杂度：O(n)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">containsValue</span><span class="params">(Object value)</span> &#123;</span><br><span class="line">        Node&lt;K,V&gt;[] tab; V v;</span><br><span class="line">        <span class="keyword">if</span> ((tab = table) != <span class="literal">null</span> &amp;&amp; size &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; tab.length; ++i) &#123;</span><br><span class="line">                <span class="keyword">for</span> (Node&lt;K,V&gt; e = tab[i]; e != <span class="literal">null</span>; e = e.next) &#123;</span><br><span class="line">                    <span class="keyword">if</span> ((v = e.value) == value ||</span><br><span class="line">                        (value != <span class="literal">null</span> &amp;&amp; value.equals(v)))</span><br><span class="line">                        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">  </span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">      下面是 keySet()、values、entrySet() 的代码部分：都是缓存性质的“视图集合”。</span></span><br><span class="line"><span class="comment">      实现方式也是一样，采用内部类继承抽象集合类（使用迭代器访问真实数据，自身不存储数据）</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> Set&lt;K&gt; <span class="title function_">keySet</span><span class="params">()</span> &#123;</span><br><span class="line">        Set&lt;K&gt; ks = keySet;</span><br><span class="line">        <span class="keyword">if</span> (ks == <span class="literal">null</span>) &#123;</span><br><span class="line">            ks = <span class="keyword">new</span> <span class="title class_">KeySet</span>();</span><br><span class="line">            keySet = ks;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ks;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">KeySet</span> <span class="keyword">extends</span> <span class="title class_">AbstractSet</span>&lt;K&gt; &#123;</span><br><span class="line">				<span class="comment">// </span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> Collection&lt;V&gt; <span class="title function_">values</span><span class="params">()</span> &#123;</span><br><span class="line">        Collection&lt;V&gt; vs = values;</span><br><span class="line">        <span class="keyword">if</span> (vs == <span class="literal">null</span>) &#123;</span><br><span class="line">            vs = <span class="keyword">new</span> <span class="title class_">Values</span>();</span><br><span class="line">            values = vs;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> vs;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">Values</span> <span class="keyword">extends</span> <span class="title class_">AbstractCollection</span>&lt;V&gt; &#123;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> Set&lt;Map.Entry&lt;K,V&gt;&gt; entrySet() &#123;</span><br><span class="line">        Set&lt;Map.Entry&lt;K,V&gt;&gt; es;</span><br><span class="line">        <span class="keyword">return</span> (es = entrySet) == <span class="literal">null</span> ? (entrySet = <span class="keyword">new</span> <span class="title class_">EntrySet</span>()) : es;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">EntrySet</span> <span class="keyword">extends</span> <span class="title class_">AbstractSet</span>&lt;Map.Entry&lt;K,V&gt;&gt; &#123;</span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="链表树化的具体实现类"><a href="#链表树化的具体实现类" class="headerlink" title="链表树化的具体实现类"></a>链表树化的具体实现类</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* ------------------------------------------------------------ */</span></span><br><span class="line"><span class="comment">// Tree bins</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 树箱的entry。 扩展 LinkedHashMap.Entry（进而扩展Node），因此可以用作常规链表节点的扩展。</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">TreeNode</span>&lt;K,V&gt; <span class="keyword">extends</span> <span class="title class_">LinkedHashMap</span>.Entry&lt;K,V&gt; &#123;</span><br><span class="line">   </span><br><span class="line">    <span class="comment">// 和标准红黑树字段设计相比，多了前项、后向引用</span></span><br><span class="line">    </span><br><span class="line">    TreeNode&lt;K,V&gt; parent;  <span class="comment">// red-black tree links</span></span><br><span class="line">    TreeNode&lt;K,V&gt; left;</span><br><span class="line">    TreeNode&lt;K,V&gt; right;</span><br><span class="line">    TreeNode&lt;K,V&gt; prev; </span><br><span class="line">    <span class="type">boolean</span> red;</span><br><span class="line">    TreeNode(<span class="type">int</span> hash, K key, V val, Node&lt;K,V&gt; next) &#123;</span><br><span class="line">        <span class="built_in">super</span>(hash, key, val, next);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Returns root of tree containing this node.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">final</span> TreeNode&lt;K,V&gt; <span class="title function_">root</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (TreeNode&lt;K,V&gt; r = <span class="built_in">this</span>, p;;) &#123;</span><br><span class="line">            <span class="keyword">if</span> ((p = r.parent) == <span class="literal">null</span>)</span><br><span class="line">                <span class="keyword">return</span> r;</span><br><span class="line">            r = p;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Ensures that the given root is the first node of its bin.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">static</span> &lt;K,V&gt; <span class="keyword">void</span> <span class="title function_">moveRootToFront</span><span class="params">(Node&lt;K,V&gt;[] tab, TreeNode&lt;K,V&gt; root)</span> &#123;</span><br><span class="line">        <span class="type">int</span> n;</span><br><span class="line">        <span class="keyword">if</span> (root != <span class="literal">null</span> &amp;&amp; tab != <span class="literal">null</span> &amp;&amp; (n = tab.length) &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> (n - <span class="number">1</span>) &amp; root.hash;</span><br><span class="line">            TreeNode&lt;K,V&gt; first = (TreeNode&lt;K,V&gt;)tab[index];</span><br><span class="line">            <span class="keyword">if</span> (root != first) &#123;</span><br><span class="line">                Node&lt;K,V&gt; rn;</span><br><span class="line">                tab[index] = root;</span><br><span class="line">                TreeNode&lt;K,V&gt; rp = root.prev;</span><br><span class="line">                <span class="keyword">if</span> ((rn = root.next) != <span class="literal">null</span>)</span><br><span class="line">                    ((TreeNode&lt;K,V&gt;)rn).prev = rp;</span><br><span class="line">                <span class="keyword">if</span> (rp != <span class="literal">null</span>)</span><br><span class="line">                    rp.next = rn;</span><br><span class="line">                <span class="keyword">if</span> (first != <span class="literal">null</span>)</span><br><span class="line">                    first.prev = root;</span><br><span class="line">                root.next = first;</span><br><span class="line">                root.prev = <span class="literal">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">assert</span> <span class="title function_">checkInvariants</span><span class="params">(root)</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Finds the node starting at root p with the given hash and key.</span></span><br><span class="line"><span class="comment">     * The kc argument caches comparableClassFor(key) upon first use</span></span><br><span class="line"><span class="comment">     * comparing keys.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">final</span> TreeNode&lt;K,V&gt; <span class="title function_">find</span><span class="params">(<span class="type">int</span> h, Object k, Class&lt;?&gt; kc)</span> &#123;</span><br><span class="line">        TreeNode&lt;K,V&gt; p = <span class="built_in">this</span>;</span><br><span class="line">        <span class="keyword">do</span> &#123;</span><br><span class="line">            <span class="type">int</span> ph, dir; K pk;</span><br><span class="line">            TreeNode&lt;K,V&gt; pl = p.left, pr = p.right, q;</span><br><span class="line">            <span class="keyword">if</span> ((ph = p.hash) &gt; h)</span><br><span class="line">                p = pl;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (ph &lt; h)</span><br><span class="line">                p = pr;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> ((pk = p.key) == k || (k != <span class="literal">null</span> &amp;&amp; k.equals(pk)))</span><br><span class="line">                <span class="keyword">return</span> p;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (pl == <span class="literal">null</span>)</span><br><span class="line">                p = pr;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (pr == <span class="literal">null</span>)</span><br><span class="line">                p = pl;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> ((kc != <span class="literal">null</span> ||</span><br><span class="line">                      (kc = comparableClassFor(k)) != <span class="literal">null</span>) &amp;&amp;</span><br><span class="line">                     (dir = compareComparables(kc, k, pk)) != <span class="number">0</span>)</span><br><span class="line">                p = (dir &lt; <span class="number">0</span>) ? pl : pr;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> ((q = pr.find(h, k, kc)) != <span class="literal">null</span>)</span><br><span class="line">                <span class="keyword">return</span> q;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                p = pl;</span><br><span class="line">        &#125; <span class="keyword">while</span> (p != <span class="literal">null</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Calls find for root node.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">final</span> TreeNode&lt;K,V&gt; <span class="title function_">getTreeNode</span><span class="params">(<span class="type">int</span> h, Object k)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> ((parent != <span class="literal">null</span>) ? root() : <span class="built_in">this</span>).find(h, k, <span class="literal">null</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Tie-breaking utility for ordering insertions when equal</span></span><br><span class="line"><span class="comment">     * hashCodes and non-comparable. We don&#x27;t require a total</span></span><br><span class="line"><span class="comment">     * order, just a consistent insertion rule to maintain</span></span><br><span class="line"><span class="comment">     * equivalence across rebalancings. Tie-breaking further than</span></span><br><span class="line"><span class="comment">     * necessary simplifies testing a bit.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">tieBreakOrder</span><span class="params">(Object a, Object b)</span> &#123;</span><br><span class="line">        <span class="type">int</span> d;</span><br><span class="line">        <span class="keyword">if</span> (a == <span class="literal">null</span> || b == <span class="literal">null</span> ||</span><br><span class="line">            (d = a.getClass().getName().</span><br><span class="line">             compareTo(b.getClass().getName())) == <span class="number">0</span>)</span><br><span class="line">            d = (System.identityHashCode(a) &lt;= System.identityHashCode(b) ?</span><br><span class="line">                 -<span class="number">1</span> : <span class="number">1</span>);</span><br><span class="line">        <span class="keyword">return</span> d;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Forms tree of the nodes linked from this node.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">treeify</span><span class="params">(Node&lt;K,V&gt;[] tab)</span> &#123;</span><br><span class="line">        TreeNode&lt;K,V&gt; root = <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">for</span> (TreeNode&lt;K,V&gt; x = <span class="built_in">this</span>, next; x != <span class="literal">null</span>; x = next) &#123;</span><br><span class="line">            next = (TreeNode&lt;K,V&gt;)x.next;</span><br><span class="line">            x.left = x.right = <span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">if</span> (root == <span class="literal">null</span>) &#123;</span><br><span class="line">                x.parent = <span class="literal">null</span>;</span><br><span class="line">                x.red = <span class="literal">false</span>;</span><br><span class="line">                root = x;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="type">K</span> <span class="variable">k</span> <span class="operator">=</span> x.key;</span><br><span class="line">                <span class="type">int</span> <span class="variable">h</span> <span class="operator">=</span> x.hash;</span><br><span class="line">                Class&lt;?&gt; kc = <span class="literal">null</span>;</span><br><span class="line">                <span class="keyword">for</span> (TreeNode&lt;K,V&gt; p = root;;) &#123;</span><br><span class="line">                    <span class="type">int</span> dir, ph;</span><br><span class="line">                    <span class="type">K</span> <span class="variable">pk</span> <span class="operator">=</span> p.key;</span><br><span class="line">                    <span class="keyword">if</span> ((ph = p.hash) &gt; h)</span><br><span class="line">                        dir = -<span class="number">1</span>;</span><br><span class="line">                    <span class="keyword">else</span> <span class="keyword">if</span> (ph &lt; h)</span><br><span class="line">                        dir = <span class="number">1</span>;</span><br><span class="line">                    <span class="keyword">else</span> <span class="keyword">if</span> ((kc == <span class="literal">null</span> &amp;&amp;</span><br><span class="line">                              (kc = comparableClassFor(k)) == <span class="literal">null</span>) ||</span><br><span class="line">                             (dir = compareComparables(kc, k, pk)) == <span class="number">0</span>)</span><br><span class="line">                        dir = tieBreakOrder(k, pk);</span><br><span class="line"></span><br><span class="line">                    TreeNode&lt;K,V&gt; xp = p;</span><br><span class="line">                    <span class="keyword">if</span> ((p = (dir &lt;= <span class="number">0</span>) ? p.left : p.right) == <span class="literal">null</span>) &#123;</span><br><span class="line">                        x.parent = xp;</span><br><span class="line">                        <span class="keyword">if</span> (dir &lt;= <span class="number">0</span>)</span><br><span class="line">                            xp.left = x;</span><br><span class="line">                        <span class="keyword">else</span></span><br><span class="line">                            xp.right = x;</span><br><span class="line">                        root = balanceInsertion(root, x);</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        moveRootToFront(tab, root);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Returns a list of non-TreeNodes replacing those linked from</span></span><br><span class="line"><span class="comment">     * this node.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">final</span> Node&lt;K,V&gt; <span class="title function_">untreeify</span><span class="params">(HashMap&lt;K,V&gt; map)</span> &#123;</span><br><span class="line">        Node&lt;K,V&gt; hd = <span class="literal">null</span>, tl = <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">for</span> (Node&lt;K,V&gt; q = <span class="built_in">this</span>; q != <span class="literal">null</span>; q = q.next) &#123;</span><br><span class="line">            Node&lt;K,V&gt; p = map.replacementNode(q, <span class="literal">null</span>);</span><br><span class="line">            <span class="keyword">if</span> (tl == <span class="literal">null</span>)</span><br><span class="line">                hd = p;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                tl.next = p;</span><br><span class="line">            tl = p;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> hd;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Tree version of putVal.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">final</span> TreeNode&lt;K,V&gt; <span class="title function_">putTreeVal</span><span class="params">(HashMap&lt;K,V&gt; map, Node&lt;K,V&gt;[] tab,</span></span><br><span class="line"><span class="params">                                   <span class="type">int</span> h, K k, V v)</span> &#123;</span><br><span class="line">        Class&lt;?&gt; kc = <span class="literal">null</span>;</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">searched</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">        TreeNode&lt;K,V&gt; root = (parent != <span class="literal">null</span>) ? root() : <span class="built_in">this</span>;</span><br><span class="line">        <span class="keyword">for</span> (TreeNode&lt;K,V&gt; p = root;;) &#123;</span><br><span class="line">            <span class="type">int</span> dir, ph; K pk;</span><br><span class="line">            <span class="keyword">if</span> ((ph = p.hash) &gt; h)</span><br><span class="line">                dir = -<span class="number">1</span>;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (ph &lt; h)</span><br><span class="line">                dir = <span class="number">1</span>;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> ((pk = p.key) == k || (k != <span class="literal">null</span> &amp;&amp; k.equals(pk)))</span><br><span class="line">                <span class="comment">// 如果找到直接返回</span></span><br><span class="line">                <span class="keyword">return</span> p;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> ((kc == <span class="literal">null</span> &amp;&amp;</span><br><span class="line">                      (kc = comparableClassFor(k)) == <span class="literal">null</span>) ||</span><br><span class="line">                     (dir = compareComparables(kc, k, pk)) == <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (!searched) &#123;</span><br><span class="line">                    TreeNode&lt;K,V&gt; q, ch;</span><br><span class="line">                    searched = <span class="literal">true</span>;</span><br><span class="line">                    <span class="keyword">if</span> (((ch = p.left) != <span class="literal">null</span> &amp;&amp;</span><br><span class="line">                         (q = ch.find(h, k, kc)) != <span class="literal">null</span>) ||</span><br><span class="line">                        ((ch = p.right) != <span class="literal">null</span> &amp;&amp;</span><br><span class="line">                         (q = ch.find(h, k, kc)) != <span class="literal">null</span>))</span><br><span class="line">                        <span class="keyword">return</span> q;</span><br><span class="line">                &#125;</span><br><span class="line">                dir = tieBreakOrder(k, pk);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            TreeNode&lt;K,V&gt; xp = p;</span><br><span class="line">            <span class="keyword">if</span> ((p = (dir &lt;= <span class="number">0</span>) ? p.left : p.right) == <span class="literal">null</span>) &#123;</span><br><span class="line">                Node&lt;K,V&gt; xpn = xp.next;</span><br><span class="line">                TreeNode&lt;K,V&gt; x = map.newTreeNode(h, k, v, xpn);</span><br><span class="line">                <span class="keyword">if</span> (dir &lt;= <span class="number">0</span>)</span><br><span class="line">                    xp.left = x;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    xp.right = x;</span><br><span class="line">                xp.next = x;</span><br><span class="line">                x.parent = x.prev = xp;</span><br><span class="line">                <span class="keyword">if</span> (xpn != <span class="literal">null</span>)</span><br><span class="line">                    ((TreeNode&lt;K,V&gt;)xpn).prev = x;</span><br><span class="line">                moveRootToFront(tab, balanceInsertion(root, x));</span><br><span class="line">                <span class="comment">// 如果是新插入的节点，返回 null</span></span><br><span class="line">                <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Removes the given node, that must be present before this call.</span></span><br><span class="line"><span class="comment">     * This is messier than typical red-black deletion code because we</span></span><br><span class="line"><span class="comment">     * cannot swap the contents of an interior node with a leaf</span></span><br><span class="line"><span class="comment">     * successor that is pinned by &quot;next&quot; pointers that are accessible</span></span><br><span class="line"><span class="comment">     * independently during traversal. So instead we swap the tree</span></span><br><span class="line"><span class="comment">     * linkages. If the current tree appears to have too few nodes,</span></span><br><span class="line"><span class="comment">     * the bin is converted back to a plain bin. (The test triggers</span></span><br><span class="line"><span class="comment">     * somewhere between 2 and 6 nodes, depending on tree structure).</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">removeTreeNode</span><span class="params">(HashMap&lt;K,V&gt; map, Node&lt;K,V&gt;[] tab,</span></span><br><span class="line"><span class="params">                              <span class="type">boolean</span> movable)</span> &#123;</span><br><span class="line">        <span class="type">int</span> n;</span><br><span class="line">        <span class="keyword">if</span> (tab == <span class="literal">null</span> || (n = tab.length) == <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> (n - <span class="number">1</span>) &amp; hash;</span><br><span class="line">        TreeNode&lt;K,V&gt; first = (TreeNode&lt;K,V&gt;)tab[index], root = first, rl;</span><br><span class="line">        TreeNode&lt;K,V&gt; succ = (TreeNode&lt;K,V&gt;)next, pred = prev;</span><br><span class="line">        <span class="keyword">if</span> (pred == <span class="literal">null</span>)</span><br><span class="line">            tab[index] = first = succ;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            pred.next = succ;</span><br><span class="line">        <span class="keyword">if</span> (succ != <span class="literal">null</span>)</span><br><span class="line">            succ.prev = pred;</span><br><span class="line">        <span class="keyword">if</span> (first == <span class="literal">null</span>)</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        <span class="keyword">if</span> (root.parent != <span class="literal">null</span>)</span><br><span class="line">            root = root.root();</span><br><span class="line">        <span class="keyword">if</span> (root == <span class="literal">null</span></span><br><span class="line">            || (movable</span><br><span class="line">                &amp;&amp; (root.right == <span class="literal">null</span></span><br><span class="line">                    || (rl = root.left) == <span class="literal">null</span></span><br><span class="line">                    || rl.left == <span class="literal">null</span>))) &#123;</span><br><span class="line">            tab[index] = first.untreeify(map);  <span class="comment">// too small</span></span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        TreeNode&lt;K,V&gt; p = <span class="built_in">this</span>, pl = left, pr = right, replacement;</span><br><span class="line">        <span class="keyword">if</span> (pl != <span class="literal">null</span> &amp;&amp; pr != <span class="literal">null</span>) &#123;</span><br><span class="line">            TreeNode&lt;K,V&gt; s = pr, sl;</span><br><span class="line">            <span class="keyword">while</span> ((sl = s.left) != <span class="literal">null</span>) <span class="comment">// find successor</span></span><br><span class="line">                s = sl;</span><br><span class="line">            <span class="type">boolean</span> <span class="variable">c</span> <span class="operator">=</span> s.red; s.red = p.red; p.red = c; <span class="comment">// swap colors</span></span><br><span class="line">            TreeNode&lt;K,V&gt; sr = s.right;</span><br><span class="line">            TreeNode&lt;K,V&gt; pp = p.parent;</span><br><span class="line">            <span class="keyword">if</span> (s == pr) &#123; <span class="comment">// p was s&#x27;s direct parent</span></span><br><span class="line">                p.parent = s;</span><br><span class="line">                s.right = p;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                TreeNode&lt;K,V&gt; sp = s.parent;</span><br><span class="line">                <span class="keyword">if</span> ((p.parent = sp) != <span class="literal">null</span>) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (s == sp.left)</span><br><span class="line">                        sp.left = p;</span><br><span class="line">                    <span class="keyword">else</span></span><br><span class="line">                        sp.right = p;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> ((s.right = pr) != <span class="literal">null</span>)</span><br><span class="line">                    pr.parent = s;</span><br><span class="line">            &#125;</span><br><span class="line">            p.left = <span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">if</span> ((p.right = sr) != <span class="literal">null</span>)</span><br><span class="line">                sr.parent = p;</span><br><span class="line">            <span class="keyword">if</span> ((s.left = pl) != <span class="literal">null</span>)</span><br><span class="line">                pl.parent = s;</span><br><span class="line">            <span class="keyword">if</span> ((s.parent = pp) == <span class="literal">null</span>)</span><br><span class="line">                root = s;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (p == pp.left)</span><br><span class="line">                pp.left = s;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                pp.right = s;</span><br><span class="line">            <span class="keyword">if</span> (sr != <span class="literal">null</span>)</span><br><span class="line">                replacement = sr;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                replacement = p;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (pl != <span class="literal">null</span>)</span><br><span class="line">            replacement = pl;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (pr != <span class="literal">null</span>)</span><br><span class="line">            replacement = pr;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            replacement = p;</span><br><span class="line">        <span class="keyword">if</span> (replacement != p) &#123;</span><br><span class="line">            TreeNode&lt;K,V&gt; pp = replacement.parent = p.parent;</span><br><span class="line">            <span class="keyword">if</span> (pp == <span class="literal">null</span>)</span><br><span class="line">                root = replacement;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (p == pp.left)</span><br><span class="line">                pp.left = replacement;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                pp.right = replacement;</span><br><span class="line">            p.left = p.right = p.parent = <span class="literal">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        TreeNode&lt;K,V&gt; r = p.red ? root : balanceDeletion(root, replacement);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (replacement == p) &#123;  <span class="comment">// detach</span></span><br><span class="line">            TreeNode&lt;K,V&gt; pp = p.parent;</span><br><span class="line">            p.parent = <span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">if</span> (pp != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (p == pp.left)</span><br><span class="line">                    pp.left = <span class="literal">null</span>;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (p == pp.right)</span><br><span class="line">                    pp.right = <span class="literal">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (movable)</span><br><span class="line">            moveRootToFront(tab, r);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Splits nodes in a tree bin into lower and upper tree bins,</span></span><br><span class="line"><span class="comment">     * or untreeifies if now too small. Called only from resize;</span></span><br><span class="line"><span class="comment">     * see above discussion about split bits and indices.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> map the map</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> tab the table for recording bin heads</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> index the index of the table being split</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> bit the bit of hash to split on</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">split</span><span class="params">(HashMap&lt;K,V&gt; map, Node&lt;K,V&gt;[] tab, <span class="type">int</span> index, <span class="type">int</span> bit)</span> &#123;</span><br><span class="line">        TreeNode&lt;K,V&gt; b = <span class="built_in">this</span>;</span><br><span class="line">        <span class="comment">// Relink into lo and hi lists, preserving order</span></span><br><span class="line">        TreeNode&lt;K,V&gt; loHead = <span class="literal">null</span>, loTail = <span class="literal">null</span>;</span><br><span class="line">        TreeNode&lt;K,V&gt; hiHead = <span class="literal">null</span>, hiTail = <span class="literal">null</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">lc</span> <span class="operator">=</span> <span class="number">0</span>, hc = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (TreeNode&lt;K,V&gt; e = b, next; e != <span class="literal">null</span>; e = next) &#123;</span><br><span class="line">            next = (TreeNode&lt;K,V&gt;)e.next;</span><br><span class="line">            e.next = <span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">if</span> ((e.hash &amp; bit) == <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> ((e.prev = loTail) == <span class="literal">null</span>)</span><br><span class="line">                    loHead = e;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    loTail.next = e;</span><br><span class="line">                loTail = e;</span><br><span class="line">                ++lc;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> ((e.prev = hiTail) == <span class="literal">null</span>)</span><br><span class="line">                    hiHead = e;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    hiTail.next = e;</span><br><span class="line">                hiTail = e;</span><br><span class="line">                ++hc;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (loHead != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (lc &lt;= UNTREEIFY_THRESHOLD)</span><br><span class="line">                tab[index] = loHead.untreeify(map);</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                tab[index] = loHead;</span><br><span class="line">                <span class="keyword">if</span> (hiHead != <span class="literal">null</span>) <span class="comment">// (else is already treeified)</span></span><br><span class="line">                    loHead.treeify(tab);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (hiHead != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (hc &lt;= UNTREEIFY_THRESHOLD)</span><br><span class="line">                tab[index + bit] = hiHead.untreeify(map);</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                tab[index + bit] = hiHead;</span><br><span class="line">                <span class="keyword">if</span> (loHead != <span class="literal">null</span>)</span><br><span class="line">                    hiHead.treeify(tab);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* ------------------------------------------------------------ */</span></span><br><span class="line">    <span class="comment">// 红黑树的算法，全部来自于《算法导论》，省略代码</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> &lt;K,V&gt; TreeNode&lt;K,V&gt; <span class="title function_">rotateLeft</span><span class="params">(TreeNode&lt;K,V&gt; root,</span></span><br><span class="line"><span class="params">                                          TreeNode&lt;K,V&gt; p)</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> &lt;K,V&gt; TreeNode&lt;K,V&gt; <span class="title function_">rotateRight</span><span class="params">(TreeNode&lt;K,V&gt; root,</span></span><br><span class="line"><span class="params">                                           TreeNode&lt;K,V&gt; p)</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> &lt;K,V&gt; TreeNode&lt;K,V&gt; <span class="title function_">balanceInsertion</span><span class="params">(TreeNode&lt;K,V&gt; root,</span></span><br><span class="line"><span class="params">                                                TreeNode&lt;K,V&gt; x)</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> &lt;K,V&gt; TreeNode&lt;K,V&gt; <span class="title function_">balanceDeletion</span><span class="params">(TreeNode&lt;K,V&gt; root,</span></span><br><span class="line"><span class="params">    &#125;</span></span><br><span class="line"><span class="params">    </span></span><br><span class="line"><span class="params">    <span class="keyword">static</span> &lt;K,V&gt; <span class="type">boolean</span> checkInvariants(TreeNode&lt;K,V&gt; t)</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="迭代器实现"><a href="#迭代器实现" class="headerlink" title="迭代器实现"></a>迭代器实现</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* ------------------------------------------------------------ */</span></span><br><span class="line"><span class="comment">// iterators</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">HashIterator</span> &#123;</span><br><span class="line">    Node&lt;K,V&gt; next;        <span class="comment">// 下一个返回的节点</span></span><br><span class="line">    Node&lt;K,V&gt; current;     <span class="comment">// 当前节点</span></span><br><span class="line">    <span class="type">int</span> expectedModCount;  <span class="comment">// fast-fail 机制 </span></span><br><span class="line">    <span class="type">int</span> index;             <span class="comment">// current slot</span></span><br><span class="line"></span><br><span class="line">    HashIterator() &#123;</span><br><span class="line">        expectedModCount = modCount;</span><br><span class="line">        Node&lt;K,V&gt;[] t = table;</span><br><span class="line">        current = next = <span class="literal">null</span>;</span><br><span class="line">        index = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">if</span> (t != <span class="literal">null</span> &amp;&amp; size &gt; <span class="number">0</span>) &#123; <span class="comment">// 找第一个不为null的entry</span></span><br><span class="line">            <span class="keyword">do</span> &#123;&#125; <span class="keyword">while</span> (index &lt; t.length &amp;&amp; (next = t[index++]) == <span class="literal">null</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">hasNext</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> next != <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">final</span> Node&lt;K,V&gt; <span class="title function_">nextNode</span><span class="params">()</span> &#123;</span><br><span class="line">        Node&lt;K,V&gt;[] t;</span><br><span class="line">        Node&lt;K,V&gt; e = next;</span><br><span class="line">        <span class="comment">// 先检查 expectedModCount</span></span><br><span class="line">        <span class="keyword">if</span> (modCount != expectedModCount)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ConcurrentModificationException</span>();</span><br><span class="line">        <span class="keyword">if</span> (e == <span class="literal">null</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NoSuchElementException</span>();</span><br><span class="line">        </span><br><span class="line">      	<span class="comment">// 先遍历链表或者</span></span><br><span class="line">        <span class="keyword">if</span> ((next = (current = e).next) == <span class="literal">null</span> &amp;&amp; (t = table) != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">do</span> &#123;&#125; <span class="keyword">while</span> (index &lt; t.length &amp;&amp; (next = t[index++]) == <span class="literal">null</span>); </span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> e;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 调用 final removeNode的方法</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">remove</span><span class="params">()</span> &#123;</span><br><span class="line">        Node&lt;K,V&gt; p = current;</span><br><span class="line">        <span class="keyword">if</span> (p == <span class="literal">null</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>();</span><br><span class="line">        <span class="keyword">if</span> (modCount != expectedModCount) <span class="comment">// 检查 expectedModCount</span></span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ConcurrentModificationException</span>();</span><br><span class="line">        current = <span class="literal">null</span>;</span><br><span class="line">        <span class="type">K</span> <span class="variable">key</span> <span class="operator">=</span> p.key;</span><br><span class="line">        removeNode(hash(key), key, <span class="literal">null</span>, <span class="literal">false</span>, <span class="literal">false</span>);</span><br><span class="line">        expectedModCount = modCount;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> keySet、values、entrySet的迭代器直接继承 HashIterator 即可</span></span><br><span class="line"><span class="comment">*/</span> </span><br><span class="line">  </span><br><span class="line"><span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">KeyIterator</span> <span class="keyword">extends</span> <span class="title class_">HashIterator</span></span><br><span class="line">    <span class="keyword">implements</span> <span class="title class_">Iterator</span>&lt;K&gt; &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> K <span class="title function_">next</span><span class="params">()</span> &#123; <span class="keyword">return</span> nextNode().key; &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">ValueIterator</span> <span class="keyword">extends</span> <span class="title class_">HashIterator</span></span><br><span class="line">    <span class="keyword">implements</span> <span class="title class_">Iterator</span>&lt;V&gt; &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> V <span class="title function_">next</span><span class="params">()</span> &#123; <span class="keyword">return</span> nextNode().value; &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">EntryIterator</span> <span class="keyword">extends</span> <span class="title class_">HashIterator</span></span><br><span class="line">    <span class="keyword">implements</span> <span class="title class_">Iterator</span>&lt;Map.Entry&lt;K,V&gt;&gt; &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> Map.Entry&lt;K,V&gt; <span class="title function_">next</span><span class="params">()</span> &#123; <span class="keyword">return</span> nextNode(); &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>







<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/qq_36334929/article/details/117913430">Java学习笔记——HashMap底层源码分析</a></p>
<p><a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/40515974">散列算法</a></p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/08/02/%E5%AF%B9Jsch%E7%9A%84Seession%E4%B8%8EChannel%E7%9A%84%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E6%80%A7%E7%9A%84%E6%B5%8B%E8%AF%95/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/08/02/%E5%AF%B9Jsch%E7%9A%84Seession%E4%B8%8EChannel%E7%9A%84%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E6%80%A7%E7%9A%84%E6%B5%8B%E8%AF%95/" class="post-title-link" itemprop="url">对Jsch的Seession与Channel的线程安全性的测试</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2021-08-02 00:00:00 / 修改时间：13:51:34" itemprop="dateCreated datePublished" datetime="2021-08-02T00:00:00+08:00">2021-08-02</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/FTP/" itemprop="url" rel="index"><span itemprop="name">FTP</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <ul>
<li><ul>
<li>10000个文件(每个文件只有 12 byte)</li>
</ul>
<ol>
<li>1 session 1 channel<br>串行： 37 s<br>并行(4 核)： 死锁<br>或者 throw new SftpException()、at com.jcraft.jsch.ChannelSftp._realpath(ChannelSftp.java:2362)<br>等各种异常</li>
<li>1 session n channels<br>串行：200 s<br>并行(4 核)：25 s (但是文件下载有丢失)</li>
<li>n sessions 1 channel<br>串行：1200 s<br>并行(4 核)：320 s</li>
</ol>
</li>
<li><p>100个文件(每个文件有 12 MB)</p>
<ol>
<li><p>1 session 1 channel<br> 串行： 19 s<br>  并行(4 核)： 死锁<br>  或者 throw new SftpException()、at com.jcraft.jsch.ChannelSftp._realpath(ChannelSftp.java:2362)<br>  等各种异常, 总之文件传输失败</p>
</li>
<li><p>1 session n channels<br>串行：20 s<br>并行(4 核)：18s (但是文件下载有丢失)</p>
</li>
<li><p>n sessions 1 channel<br>串行：35 s<br>并行(4 核)：10 s</p>
</li>
</ol>
</li>
<li><p>10个文件(每个文件有 120 MB)</p>
<ol>
<li><p>1 session 1 channel<br>串行： 18 s<br>并行(4 核)： 死锁<br>或者 throw new SftpException()、at com.jcraft.jsch.ChannelSftp._realpath(ChannelSftp.java:2362)<br>等各种异常, 总之文件传输失败</p>
</li>
<li><p>1 session n channels<br>串行：18 s<br>并行(4 核)：19 s </p>
</li>
<li><p>n sessions 1 channel<br>串行：19 s<br>并行(4 核)：8 s</p>
</li>
</ol>
</li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/08/01/Spring%20%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B04%20%20%E2%80%94%E2%80%94%20%20AOP%EF%BC%88%E9%9D%A2%E5%90%91%E5%88%87%E9%9D%A2%E7%BC%96%E7%A8%8B%EF%BC%89/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/08/01/Spring%20%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B04%20%20%E2%80%94%E2%80%94%20%20AOP%EF%BC%88%E9%9D%A2%E5%90%91%E5%88%87%E9%9D%A2%E7%BC%96%E7%A8%8B%EF%BC%89/" class="post-title-link" itemprop="url">Spring 学习笔记4  ——  AOP（面向切面编程）</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-08-01 00:00:00" itemprop="dateCreated datePublished" datetime="2021-08-01T00:00:00+08:00">2021-08-01</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-10-15 00:41:25" itemprop="dateModified" datetime="2021-10-15T00:41:25+08:00">2021-10-15</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Spring/" itemprop="url" rel="index"><span itemprop="name">Spring</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <blockquote>
<p>这里我做的这份笔记的意义仅仅是记录下自己学习Spring的历程，既不是对官方API的深入解释，也不一定是对新手友好的、知识点详尽的一份学习清单。</p>
</blockquote>
<h1 id="5-Spring-面向方面编程"><a href="#5-Spring-面向方面编程" class="headerlink" title="5. Spring 面向方面编程"></a>5. Spring 面向方面编程</h1><h2 id="5-1-AOP概念"><a href="#5-1-AOP概念" class="headerlink" title="5.1 AOP概念"></a>5.1 AOP概念</h2><p>下面是一些核心 AOP 概念和术语。这些术语不是特定于 Spring 的。</p>
<ul>
<li><p>切面（Aspect）：跨越多个类的关注点的模块化—— <strong>AOP中的基本单位</strong>，正如OOP中的类一样。在 Spring AOP 中，方面是通过使用<strong>基于XML配置常规Bean</strong>或<strong>使用注解@Aspect注解的常规类 （@AspectJ 风格）实现的</strong>。</p>
<p>注意：<code>@Aspect</code>是注解，@AspectJ是AspectJ project在Spring AOP中实现的风格。</p>
</li>
<li><p>连接点（Join point）：<strong>程序执行过程中的一个点，例如方法的执行或异常的处理</strong>。</p>
<p>在 Spring AOP 中，一个连接点总是代表一个<strong>方法</strong>的执行，目前不能对<strong>字段</strong>进行拦截。</p>
</li>
<li><p>通知（Advice）：<strong>方面在特定连接点采取的行动</strong>。通知类型将在后面讨论。</p>
</li>
<li><p>切点（Pointcut）：<strong>匹配连接点的谓词。</strong>由切入点表达式匹配的连接点的概念是 AOP 的核心，Spring 默认使用 AspectJ 风格的切入点表达式语言。</p>
<p>通俗的说，Spring会根据切点定义的规则去匹配连接点。</p>
</li>
<li><p>引入（Introduction） ：代表类型声明额外的方法或字段。Spring AOP 允许您向任何目标对象引入新的接口（和相应的实现）。</p>
<p>这个算是AOP里比较难理解的概念，最好通过实例来解释。</p>
</li>
<li><p>目标对象（Target object）：被一个或多个方面建议的对象。也称为“建议对象”。由于 Spring AOP 是使用运行时代理实现的，所以这个对象始终是一个<strong>被代理的对象</strong> （即原对象）。</p>
</li>
<li><p>AOP 代理：由 AOP 框架创建的对象，用于实现方面契约（建议方法执行等）。在 Spring Framework 中，AOP 代理由 JDK 动态代理或 CGLIB 代理。</p>
</li>
<li><p>编织：将切面与其他应用程序类型或对象联系起来以创建 Advised 对象。</p>
</li>
</ul>
<p>切入点匹配的连接点概念是 AOP 的关键，它区别于仅提供拦截器的技术。</p>
<p>Spring AOP 包括以下类型的通知</p>
<ul>
<li>前置（Before）通知：在连接点之前运行的通知，<strong>但不能阻止执行流继续到连接点</strong>（除非它抛出异常）。</li>
<li>返回后（After returning）通知：在连接点正常完成后运行的通知（例如，如果方法返回而没有抛出异常）。</li>
<li>抛出异常后（After throwing）通知：如果方法通过抛出异常退出，则运行通知。</li>
<li>后置（After or finally）通知：不管连接点退出的方式（正常或异常返回）都将运行的通知。</li>
<li>环绕（Around）通知：环绕连接点的通知。这是最有力的通知。<strong>环绕通知可以在方法调用之前和之后执行自定义行为</strong>。它还<strong>负责选择是继续连接点还是通过返回自己的返回值或抛出异常来缩短被代理的方法执行</strong>。</li>
</ul>
<h2 id="5-2-使用-AspectJ风格的AOP"><a href="#5-2-使用-AspectJ风格的AOP" class="headerlink" title="5.2 使用@AspectJ风格的AOP"></a>5.2 使用@AspectJ风格的AOP</h2><p>AOP在Spring中的使用，既可以使用注解驱动，也可以使用XML配置。</p>
<p>这里仍然只介绍注解驱动的AOP。</p>
<h3 id="5-2-1-启用-AspectJ支持"><a href="#5-2-1-启用-AspectJ支持" class="headerlink" title="5.2.1 启用@AspectJ支持"></a>5.2.1 启用@AspectJ支持</h3><p>对于SpringBoot项目而言，引入依赖即可。</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- aop --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-aop<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>下面介绍AOP中的一系列概念在@AspectJ中的具体落地实现。</p>
<h3 id="5-2-2-声明一个切面"><a href="#5-2-2-声明一个切面" class="headerlink" title="5.2.2 声明一个切面"></a>5.2.2 声明一个切面</h3><p>使用注解 <code>@Aspect</code>标记类即可。</p>
<p>但请注意， <code>@Aspect</code>注释不足以在类路径中进行自动检测从而被Spring容器管理，所以@Component是有必要的。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NotVeryUsefulAspect</span> &#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>而且，在 Spring AOP 中，切面本身不能成为其他切面通知的目标。</p>
<h3 id="5-2-3-声明一个切入点"><a href="#5-2-3-声明一个切入点" class="headerlink" title="5.2.3 声明一个切入点"></a>5.2.3 声明一个切入点</h3><p>如下，<strong>切点是一个用<code>@Pointcut</code>标记的方法（返回值必须是void）</strong>。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Pointcut(&quot;execution(* transfer(..))&quot;)</span> <span class="comment">// the pointcut expression</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">anyOldTransfer</span><span class="params">()</span> &#123;&#125; <span class="comment">// the pointcut signature</span></span><br></pre></td></tr></table></figure>

<p>构成<code>@Pointcut</code>注解值的切入点表达式是一个正则 AspectJ 切入点表达式，这是切点的关键。</p>
<p>具体可以参考附录中的AspectJ编程指南。</p>
<blockquote>
<p>参考资料</p>
<p><a target="_blank" rel="noopener" href="https://docs.spring.io/spring-framework/docs/current-SNAPSHOT/reference/html/core.html#spring-core">Spring官方文档</a></p>
<p><a target="_blank" rel="noopener" href="https://docs.spring.io/spring-framework/docs/">doc index</a></p>
<p><a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1532547#:~:text=Spring%20AOP%20%E9%BB%98%E8%AE%A4%E4%BD%BF%E7%94%A8JDK,%E5%8F%AF%E4%BB%A5%E5%BC%BA%E5%88%B6%E4%BD%BF%E7%94%A8CGLIB%20%E4%BB%A3%E7%90%86%E3%80%82">Spring 5 AOP 默认改用 CGLIB 了？</a></p>
<p><a target="_blank" rel="noopener" href="https://www.eclipse.org/aspectj/doc/released/progguide/index.html">AspectJ编程指南</a></p>
<p><a target="_blank" rel="noopener" href="https://www.eclipse.org/aspectj/">AspectJ网站</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/08/01/Ubuntu-%E9%85%8D%E7%BD%AE-SFTP-%E6%9C%8D%E5%8A%A1%E5%99%A8/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/08/01/Ubuntu-%E9%85%8D%E7%BD%AE-SFTP-%E6%9C%8D%E5%8A%A1%E5%99%A8/" class="post-title-link" itemprop="url">Ubuntu 配置 SFTP 服务器</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-08-01 00:00:00" itemprop="dateCreated datePublished" datetime="2021-08-01T00:00:00+08:00">2021-08-01</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-12-06 15:54:18" itemprop="dateModified" datetime="2021-12-06T15:54:18+08:00">2021-12-06</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Linux/" itemprop="url" rel="index"><span itemprop="name">Linux</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="安装SFTP服务"><a href="#安装SFTP服务" class="headerlink" title="安装SFTP服务"></a>安装SFTP服务</h1><blockquote>
<p>sftp是Secure File Transfer Protocol的缩写，安全文件传送协议。可以为传输文件提供一种安全的网络的加密方法。<strong>sftp 与 ftp 有着几乎一样的语法和功能。</strong>SFTP 为 SSH的其中一部分，是一种传输档案至 Blogger 伺服器的安全方式。其实<strong>在SSH软件包中，已经包含了一个叫作SFTP(Secure File Transfer Protocol)的安全文件信息传输子系统</strong>。 <strong>SFTP本身没有单独的守护进程，它必须使用sshd守护进程（端口号默认是22）来完成相应的连接和答复操作，所以从某种意义上来说，SFTP并不像一个服务器程序，而更像是一个客户端程序。</strong>SFTP同样是使用加密传输认证信息和传输的数据，所以，使用SFTP是非常安全的。但是<strong>，由于这种传输方式使用了加密&#x2F;解密技术，所以传输效率比普通的FTP要低得多</strong>，如果您对网络安全性要求更高时，可以使用SFTP代替FTP。</p>
</blockquote>
<p>Ubuntu默认只安装openssh-client，需要手动安装openssh-server或者sftp-server。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo apt update</span><br><span class="line">sudo apt install openssh-server</span><br></pre></td></tr></table></figure>



<h1 id="目标"><a href="#目标" class="headerlink" title="目标"></a>目标</h1><p>在Ubuntu系统上开通sftp文件服务，允许某些用户上传及下载文件。</p>
<p>但是这些用户只能使用sftp传输文件，不能使用SSH终端访问服务器，并且sftp不能访问系统文件。</p>
<p>系统管理员则既能使用sftp传输文件，也能使用SSH远程管理服务器。</p>
<h1 id="操作步骤"><a href="#操作步骤" class="headerlink" title="操作步骤"></a>操作步骤</h1><h2 id="sftp用户、用户组的创建"><a href="#sftp用户、用户组的创建" class="headerlink" title="sftp用户、用户组的创建"></a>sftp用户、用户组的创建</h2><ul>
<li>添加sftp用户，创建 sftp 用户组</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">sudo adduser alice</span><br><span class="line">sudo addgroup sftp-users</span><br><span class="line"><span class="comment"># 将alice从所有其他用户组中移除并加入到sftp-users组，并且关闭其Shell访问</span></span><br><span class="line"><span class="comment">#/bin/false也可以替换为/sbin/nologin，目的是不允许该用户登录到系统中</span></span><br><span class="line">sudo usermod -G sftp-users -s /bin/false alice</span><br></pre></td></tr></table></figure>



<ul>
<li>创建sftp服务器的管理员身份，并且加入 ssh用户组（不限制他的shell操作权限）</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">sudo adduser admin</span><br><span class="line">sudo addgroup ssh-users</span><br><span class="line"><span class="comment"># -a 表示以追加形式将 admin 加入 ssh-users </span></span><br><span class="line">sudo usermod -a -G ssh-users admin</span><br></pre></td></tr></table></figure>


<h2 id="创建文件服务器目录"><a href="#创建文件服务器目录" class="headerlink" title="创建文件服务器目录"></a>创建文件服务器目录</h2><ul>
<li>创建服务器文件目录</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建监狱目录</span></span><br><span class="line">sudo <span class="built_in">mkdir</span> /home/sftp_root</span><br><span class="line"><span class="comment"># 普通用户能够写入的共享文件目录</span></span><br><span class="line">sudo <span class="built_in">mkdir</span> /home/sftp_root/shared</span><br><span class="line"><span class="comment"># 设置共享文件夹的拥有者为管理员、用户组为 sftp-users</span></span><br><span class="line">sudo <span class="built_in">chown</span> admin:sftp-users /home/sftp_root/shared</span><br><span class="line"><span class="comment"># 拥有者、sftp用户组的成员具有一切权限</span></span><br><span class="line">sudo <span class="built_in">chmod</span> 770 /home/sftp_root/shared</span><br></pre></td></tr></table></figure>



<p>默认允许所有用户或者用户组登录，若仅允许指定用户或者用户组访问，按照以下格式配置。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 以空格间隔</span></span><br><span class="line">AllowUsers user1 user2 </span><br><span class="line">AllowGroups group1 group2 </span><br></pre></td></tr></table></figure>

<h2 id="权限配置"><a href="#权限配置" class="headerlink" title="权限配置"></a>权限配置</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo vim /etc/ssh/sshd_config</span><br></pre></td></tr></table></figure>

<p>在sshd_config文件的最后，添加以下内容：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">AllowGroups ssh-users sftp-users</span><br><span class="line">Match Group sftp-users</span><br><span class="line">ChrootDirectory /home/sftp_root</span><br><span class="line">AllowTcpForwarding no</span><br><span class="line">X11Forwarding no</span><br><span class="line">ForceCommand internal-sftp</span><br></pre></td></tr></table></figure>

<p>这样配置的目的是：</p>
<ul>
<li><p>只允许ssh-uers及sftp-users通过SSH访问系统，其中ssh-uers用户组可以使用ssh，并且不受其他限制。</p>
<p>而SFTP用户组仅能使用SFTP进行访问，且不能使用shell</p>
</li>
<li><p>针对sftp-users用户，额外增加一些设置：将“&#x2F;home&#x2F;sftp_root”设置为该组用户的系统根目录（因此它们将不能访问该目录之外的其他系统文件;</p>
</li>
<li><p>禁止TCP Forwarding和X11 Forwarding</p>
</li>
</ul>
<p>另外需要注意的是：<strong>ChrootDirectory的权限问题，你设定的目录必须是root用户所有</strong>，否则就会出现问题。所以请确保sftp用户根目录的所有人是root, 权限是 750 或者 755。</p>
<p>需要注意的一点的是，现在所有需要远程连接的用户都必须加入 ssh-users 才行。</p>
<p>如将用户 songyangji 加入 ，这样ta就可以使用ssh连接本机。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo usermod -a -G ssh-users songyangji</span><br></pre></td></tr></table></figure>

<ul>
<li><strong>限制ssh连接的访问IP</strong></li>
</ul>
<p>仍然是</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo vim /etc/ssh/sshd_config</span><br></pre></td></tr></table></figure>



<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Authentication:</span></span><br><span class="line">AllowUsers root@10.10.10.*        <span class="comment">#限制root用户只能通过10.10.10.*网段登录访问</span></span><br><span class="line">AllowUsers charles@10.10.10.*        <span class="comment">#限制charles用户只能通过10.10.10.*网段登录访问</span></span><br><span class="line">AllowUsers john@10.10.10.*</span><br></pre></td></tr></table></figure>


<h1 id="重启SSH"><a href="#重启SSH" class="headerlink" title="重启SSH"></a>重启SSH</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 亦可</span></span><br><span class="line"><span class="comment"># /etc/init.d/ssh restart</span></span><br><span class="line">service ssh restart</span><br></pre></td></tr></table></figure>





<blockquote>
<p>参考博客</p>
<p><a target="_blank" rel="noopener" href="https://www.jianshu.com/p/6b588a712513">搭建sftp服务器</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/08/01/%E7%BB%88%E7%AB%AF%E8%BF%9E%E6%8E%A5%E5%B9%B6%E4%BD%BF%E7%94%A8FTP-SFTP%E6%9C%8D%E5%8A%A1%E5%99%A8/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/08/01/%E7%BB%88%E7%AB%AF%E8%BF%9E%E6%8E%A5%E5%B9%B6%E4%BD%BF%E7%94%A8FTP-SFTP%E6%9C%8D%E5%8A%A1%E5%99%A8/" class="post-title-link" itemprop="url">终端连接并使用FTP/SFTP服务器</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-08-01 00:00:00" itemprop="dateCreated datePublished" datetime="2021-08-01T00:00:00+08:00">2021-08-01</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-12-06 15:55:10" itemprop="dateModified" datetime="2021-12-06T15:55:10+08:00">2021-12-06</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/FTP/" itemprop="url" rel="index"><span itemprop="name">FTP</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <p>下面列出一些常见的终端操作命令。</p>
<table>
<thead>
<tr>
<th>针对远方服务器主机 (Server) 之行为</th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td>切换目录</td>
<td>cd path</td>
</tr>
<tr>
<td>列出目前所在目录下的文件名</td>
<td>ls (or) dir</td>
</tr>
<tr>
<td>建立目录</td>
<td>mkdir directory</td>
</tr>
<tr>
<td>删除目录</td>
<td>rmdir directory</td>
</tr>
<tr>
<td>显示目前所在的目录</td>
<td>pwd</td>
</tr>
<tr>
<td>删除档案或目录</td>
<td>rm PATH</td>
</tr>
<tr>
<td>更改档案或目录名称</td>
<td>rename oldname newname</td>
</tr>
<tr>
<td>离开远程主机</td>
<td>exit (or) bye (or) quit</td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th>针对本机 (Client) 之行为(都加上 l (L的小写) )</th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td>变换目录到本机的 PATH 当中</td>
<td>lcd PATH</td>
</tr>
<tr>
<td>列出目前本机所在目录下的文件名</td>
<td>lls</td>
</tr>
<tr>
<td>在本机建立目录</td>
<td>lmkdir  directory</td>
</tr>
<tr>
<td>显示目前所在的本机目录</td>
<td>lpwd</td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th>针对资料上传&#x2F;下载的行为</th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td>将档案由本机上传到远程主机</td>
<td>put local-file [remote-file]</td>
</tr>
<tr>
<td>将档案由远程主机下载回来</td>
<td>get remote-file [local-file]</td>
</tr>
<tr>
<td>上传多个文件件</td>
<td>mput files</td>
</tr>
<tr>
<td>下载多个文件</td>
<td>mget files</td>
</tr>
</tbody></table>
<ul>
<li>关于上传文件与文件夹的坑</li>
</ul>
<p>上传、下载文件肯定是没有问题的。</p>
<p>但是对于一整个文件夹的传输并不是像  scp命令一样通过 -r 参数就可以解决, 需要注意</p>
<blockquote>
<p>man sftp手册</p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




  <nav class="pagination">
    <a class="extend prev" rel="prev" title="上一页" aria-label="上一页" href="/page/23/"><i class="fa fa-angle-left"></i></a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/23/">23</a><span class="page-number current">24</span><a class="page-number" href="/page/25/">25</a><a class="page-number" href="/page/26/">26</a><a class="extend next" rel="next" title="下一页" aria-label="下一页" href="/page/25/"><i class="fa fa-angle-right"></i></a>
  </nav>

</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">SongyangJi</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>
  <div class="sidebar-dimmer"></div>
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up fa-lg"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/hexo-generator-searchdb/1.4.1/search.js" integrity="sha256-1kfA5uHPf65M5cphT2dvymhkuyHPQp5A53EGZOnOLmc=" crossorigin="anonymous"></script>
<script src="/js/third-party/search/local-search.js"></script>





  





</body>
</html>
